mirror of https://github.com/ginuerzh/gost
6 changed files with 189 additions and 115 deletions
@ -0,0 +1,163 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"bufio" |
||||
|
"bytes" |
||||
|
"encoding/json" |
||||
|
"io" |
||||
|
"io/ioutil" |
||||
|
"strconv" |
||||
|
"strings" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/ginuerzh/gost" |
||||
|
) |
||||
|
|
||||
|
type peerConfig struct { |
||||
|
Strategy string `json:"strategy"` |
||||
|
MaxFails int `json:"max_fails"` |
||||
|
FailTimeout time.Duration `json:"fail_timeout"` |
||||
|
period time.Duration // the period for live reloading
|
||||
|
Nodes []string `json:"nodes"` |
||||
|
group *gost.NodeGroup |
||||
|
baseNodes []gost.Node |
||||
|
} |
||||
|
|
||||
|
type bypass struct { |
||||
|
Reverse bool `json:"reverse"` |
||||
|
Patterns []string `json:"patterns"` |
||||
|
} |
||||
|
|
||||
|
func parsePeerConfig(cfg string, group *gost.NodeGroup, baseNodes []gost.Node) *peerConfig { |
||||
|
pc := &peerConfig{ |
||||
|
group: group, |
||||
|
baseNodes: baseNodes, |
||||
|
} |
||||
|
go gost.PeriodReload(pc, cfg) |
||||
|
return pc |
||||
|
} |
||||
|
|
||||
|
func (cfg *peerConfig) Validate() { |
||||
|
if cfg.MaxFails <= 0 { |
||||
|
cfg.MaxFails = 1 |
||||
|
} |
||||
|
if cfg.FailTimeout <= 0 { |
||||
|
cfg.FailTimeout = 30 // seconds
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (cfg *peerConfig) Reload(r io.Reader) error { |
||||
|
if err := cfg.parse(r); err != nil { |
||||
|
return err |
||||
|
} |
||||
|
cfg.Validate() |
||||
|
|
||||
|
group := cfg.group |
||||
|
strategy := cfg.Strategy |
||||
|
if len(cfg.baseNodes) > 0 { |
||||
|
// overwrite the strategry in the peer config if `strategy` param exists.
|
||||
|
if s := cfg.baseNodes[0].Get("strategy"); s != "" { |
||||
|
strategy = s |
||||
|
} |
||||
|
} |
||||
|
group.Options = append([]gost.SelectOption{}, |
||||
|
gost.WithFilter(&gost.FailFilter{ |
||||
|
MaxFails: cfg.MaxFails, |
||||
|
FailTimeout: time.Duration(cfg.FailTimeout) * time.Second, |
||||
|
}), |
||||
|
gost.WithStrategy(parseStrategy(strategy)), |
||||
|
) |
||||
|
|
||||
|
gNodes := cfg.baseNodes |
||||
|
nid := len(gNodes) + 1 |
||||
|
for _, s := range cfg.Nodes { |
||||
|
nodes, err := parseChainNode(s) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
for i := range nodes { |
||||
|
nodes[i].ID = nid |
||||
|
nid++ |
||||
|
} |
||||
|
|
||||
|
gNodes = append(gNodes, nodes...) |
||||
|
} |
||||
|
|
||||
|
group.SetNodes(gNodes...) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (cfg *peerConfig) parse(r io.Reader) error { |
||||
|
data, err := ioutil.ReadAll(r) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
// compatible with JSON format
|
||||
|
if err := json.NewDecoder(bytes.NewReader(data)).Decode(cfg); err == nil { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
split := func(line string) []string { |
||||
|
if line == "" { |
||||
|
return nil |
||||
|
} |
||||
|
if n := strings.IndexByte(line, '#'); n >= 0 { |
||||
|
line = line[:n] |
||||
|
} |
||||
|
line = strings.Replace(line, "\t", " ", -1) |
||||
|
line = strings.TrimSpace(line) |
||||
|
|
||||
|
var ss []string |
||||
|
for _, s := range strings.Split(line, " ") { |
||||
|
if s = strings.TrimSpace(s); s != "" { |
||||
|
ss = append(ss, s) |
||||
|
} |
||||
|
} |
||||
|
return ss |
||||
|
} |
||||
|
|
||||
|
cfg.Nodes = nil |
||||
|
scanner := bufio.NewScanner(bytes.NewReader(data)) |
||||
|
for scanner.Scan() { |
||||
|
line := scanner.Text() |
||||
|
ss := split(line) |
||||
|
if len(ss) < 2 { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
switch ss[0] { |
||||
|
case "strategy": |
||||
|
cfg.Strategy = ss[1] |
||||
|
case "max_fails": |
||||
|
cfg.MaxFails, _ = strconv.Atoi(ss[1]) |
||||
|
case "fail_timeout": |
||||
|
cfg.FailTimeout, _ = time.ParseDuration(ss[1]) |
||||
|
case "reload": |
||||
|
cfg.period, _ = time.ParseDuration(ss[1]) |
||||
|
case "peer": |
||||
|
cfg.Nodes = append(cfg.Nodes, ss[1]) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return scanner.Err() |
||||
|
} |
||||
|
|
||||
|
func (cfg *peerConfig) Period() time.Duration { |
||||
|
return cfg.period |
||||
|
} |
||||
|
|
||||
|
func parseStrategy(s string) gost.Strategy { |
||||
|
switch s { |
||||
|
case "random": |
||||
|
return &gost.RandomStrategy{} |
||||
|
case "fifo": |
||||
|
return &gost.FIFOStrategy{} |
||||
|
case "round": |
||||
|
fallthrough |
||||
|
default: |
||||
|
return &gost.RoundStrategy{} |
||||
|
} |
||||
|
} |
||||
@ -1,18 +0,0 @@ |
|||||
{ |
|
||||
"strategy": "round", |
|
||||
"max_fails": 3, |
|
||||
"fail_timeout": 30, |
|
||||
"nodes":[ |
|
||||
"socks5://:1081", |
|
||||
"socks://:1082", |
|
||||
"socks4a://:1083" |
|
||||
], |
|
||||
"bypass":{ |
|
||||
"reverse": false, |
|
||||
"patterns": [ |
|
||||
"10.0.0.1", |
|
||||
"192.168.0.0/24", |
|
||||
"*.example.com" |
|
||||
] |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,14 @@ |
|||||
|
# strategy for node selecting |
||||
|
strategy random |
||||
|
|
||||
|
max_fails 1 |
||||
|
|
||||
|
fail_timeout 30s |
||||
|
|
||||
|
# period for live reloading |
||||
|
reload 10s |
||||
|
|
||||
|
# peers |
||||
|
peer http://:18080 |
||||
|
peer socks://:11080 |
||||
|
peer ss://chacha20:123456@:18338 |
||||
Loading…
Reference in new issue