|
|
|
@ -22,10 +22,8 @@ import ( |
|
|
|
) |
|
|
|
|
|
|
|
var ( |
|
|
|
options struct { |
|
|
|
ChainNodes, ServeNodes stringList |
|
|
|
Debug bool |
|
|
|
} |
|
|
|
options route |
|
|
|
routes []route |
|
|
|
) |
|
|
|
|
|
|
|
func init() { |
|
|
|
@ -43,12 +41,17 @@ func init() { |
|
|
|
flag.BoolVar(&printVersion, "V", false, "print version") |
|
|
|
flag.Parse() |
|
|
|
|
|
|
|
if len(options.ServeNodes) > 0 { |
|
|
|
routes = append(routes, options) |
|
|
|
} |
|
|
|
gost.Debug = options.Debug |
|
|
|
|
|
|
|
if err := loadConfigureFile(configureFile); err != nil { |
|
|
|
log.Log(err) |
|
|
|
os.Exit(1) |
|
|
|
} |
|
|
|
|
|
|
|
if flag.NFlag() == 0 { |
|
|
|
if flag.NFlag() == 0 || len(routes) == 0 { |
|
|
|
flag.PrintDefaults() |
|
|
|
os.Exit(0) |
|
|
|
} |
|
|
|
@ -57,8 +60,6 @@ func init() { |
|
|
|
fmt.Fprintf(os.Stderr, "gost %s (%s)\n", gost.Version, runtime.Version()) |
|
|
|
os.Exit(0) |
|
|
|
} |
|
|
|
|
|
|
|
gost.Debug = options.Debug |
|
|
|
} |
|
|
|
|
|
|
|
func main() { |
|
|
|
@ -72,45 +73,73 @@ func main() { |
|
|
|
Certificates: []tls.Certificate{cert}, |
|
|
|
} |
|
|
|
|
|
|
|
chain, err := initChain() |
|
|
|
if err != nil { |
|
|
|
log.Log(err) |
|
|
|
os.Exit(1) |
|
|
|
} |
|
|
|
if err := serve(chain); err != nil { |
|
|
|
log.Log(err) |
|
|
|
os.Exit(1) |
|
|
|
for _, route := range routes { |
|
|
|
if err := route.serve(); err != nil { |
|
|
|
log.Log(err) |
|
|
|
os.Exit(1) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
select {} |
|
|
|
} |
|
|
|
|
|
|
|
func initChain() (*gost.Chain, error) { |
|
|
|
type route struct { |
|
|
|
ChainNodes, ServeNodes stringList |
|
|
|
Retries int |
|
|
|
Debug bool |
|
|
|
} |
|
|
|
|
|
|
|
func (r *route) initChain() (*gost.Chain, error) { |
|
|
|
chain := gost.NewChain() |
|
|
|
for _, ns := range options.ChainNodes { |
|
|
|
chain.Retries = r.Retries |
|
|
|
|
|
|
|
gid := 1 // group ID
|
|
|
|
|
|
|
|
for _, ns := range r.ChainNodes { |
|
|
|
ngroup := gost.NewNodeGroup() |
|
|
|
ngroup.ID = gid |
|
|
|
gid++ |
|
|
|
|
|
|
|
// parse the base node
|
|
|
|
node, err := parseChainNode(ns) |
|
|
|
nodes, err := parseChainNode(ns) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
ngroup := gost.NewNodeGroup(node) |
|
|
|
nid := 1 // node ID
|
|
|
|
|
|
|
|
// parse node peers if exists
|
|
|
|
peerCfg, err := loadPeerConfig(node.Values.Get("peer")) |
|
|
|
for i := range nodes { |
|
|
|
nodes[i].ID = nid |
|
|
|
nid++ |
|
|
|
} |
|
|
|
ngroup.AddNode(nodes...) |
|
|
|
|
|
|
|
// parse peer nodes if exists
|
|
|
|
peerCfg, err := loadPeerConfig(nodes[0].Values.Get("peer")) |
|
|
|
if err != nil { |
|
|
|
log.Log(err) |
|
|
|
} |
|
|
|
peerCfg.Validate() |
|
|
|
ngroup.Options = append(ngroup.Options, |
|
|
|
// gost.WithFilter(),
|
|
|
|
gost.WithFilter(&gost.FailFilter{ |
|
|
|
MaxFails: peerCfg.MaxFails, |
|
|
|
FailTimeout: time.Duration(peerCfg.FailTimeout) * time.Second, |
|
|
|
}), |
|
|
|
gost.WithStrategy(parseStrategy(peerCfg.Strategy)), |
|
|
|
) |
|
|
|
|
|
|
|
for _, s := range peerCfg.Nodes { |
|
|
|
node, err = parseChainNode(s) |
|
|
|
nodes, err = parseChainNode(s) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
ngroup.AddNode(node) |
|
|
|
|
|
|
|
for i := range nodes { |
|
|
|
nodes[i].ID = nid |
|
|
|
nid++ |
|
|
|
} |
|
|
|
|
|
|
|
ngroup.AddNode(nodes...) |
|
|
|
} |
|
|
|
|
|
|
|
chain.AddNodeGroup(ngroup) |
|
|
|
@ -119,15 +148,12 @@ func initChain() (*gost.Chain, error) { |
|
|
|
return chain, nil |
|
|
|
} |
|
|
|
|
|
|
|
func parseChainNode(ns string) (node gost.Node, err error) { |
|
|
|
node, err = gost.ParseNode(ns) |
|
|
|
func parseChainNode(ns string) (nodes []gost.Node, err error) { |
|
|
|
node, err := gost.ParseNode(ns) |
|
|
|
if err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
node.IPs = parseIP(node.Values.Get("ip")) |
|
|
|
node.IPSelector = &gost.RoundRobinIPSelector{} |
|
|
|
|
|
|
|
users, err := parseUsers(node.Values.Get("secrets")) |
|
|
|
if err != nil { |
|
|
|
return |
|
|
|
@ -135,7 +161,7 @@ func parseChainNode(ns string) (node gost.Node, err error) { |
|
|
|
if node.User == nil && len(users) > 0 { |
|
|
|
node.User = users[0] |
|
|
|
} |
|
|
|
serverName, _, _ := net.SplitHostPort(node.Addr) |
|
|
|
serverName, sport, _ := net.SplitHostPort(node.Addr) |
|
|
|
if serverName == "" { |
|
|
|
serverName = "localhost" // default server name
|
|
|
|
} |
|
|
|
@ -177,7 +203,7 @@ func parseChainNode(ns string) (node gost.Node, err error) { |
|
|
|
*/ |
|
|
|
config, err := parseKCPConfig(node.Values.Get("c")) |
|
|
|
if err != nil { |
|
|
|
return node, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
tr = gost.KCPTransporter(config) |
|
|
|
case "ssh": |
|
|
|
@ -206,7 +232,7 @@ func parseChainNode(ns string) (node gost.Node, err error) { |
|
|
|
|
|
|
|
case "obfs4": |
|
|
|
if err := gost.Obfs4Init(node, false); err != nil { |
|
|
|
return node, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
tr = gost.Obfs4Transporter() |
|
|
|
case "ohttp": |
|
|
|
@ -249,24 +275,41 @@ func parseChainNode(ns string) (node gost.Node, err error) { |
|
|
|
|
|
|
|
interval, _ := strconv.Atoi(node.Values.Get("ping")) |
|
|
|
retry, _ := strconv.Atoi(node.Values.Get("retry")) |
|
|
|
node.HandshakeOptions = append(node.HandshakeOptions, |
|
|
|
handshakeOptions := []gost.HandshakeOption{ |
|
|
|
gost.AddrHandshakeOption(node.Addr), |
|
|
|
gost.HostHandshakeOption(node.Host), |
|
|
|
gost.UserHandshakeOption(node.User), |
|
|
|
gost.TLSConfigHandshakeOption(tlsCfg), |
|
|
|
gost.IntervalHandshakeOption(time.Duration(interval)*time.Second), |
|
|
|
gost.TimeoutHandshakeOption(time.Duration(timeout)*time.Second), |
|
|
|
gost.IntervalHandshakeOption(time.Duration(interval) * time.Second), |
|
|
|
gost.TimeoutHandshakeOption(time.Duration(timeout) * time.Second), |
|
|
|
gost.RetryHandshakeOption(retry), |
|
|
|
) |
|
|
|
} |
|
|
|
node.Client = &gost.Client{ |
|
|
|
Connector: connector, |
|
|
|
Transporter: tr, |
|
|
|
} |
|
|
|
|
|
|
|
ips := parseIP(node.Values.Get("ip"), sport) |
|
|
|
for _, ip := range ips { |
|
|
|
node.Addr = ip |
|
|
|
node.HandshakeOptions = append(handshakeOptions, gost.AddrHandshakeOption(ip)) |
|
|
|
nodes = append(nodes, node) |
|
|
|
} |
|
|
|
if len(ips) == 0 { |
|
|
|
node.HandshakeOptions = handshakeOptions |
|
|
|
nodes = []gost.Node{node} |
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
func serve(chain *gost.Chain) error { |
|
|
|
for _, ns := range options.ServeNodes { |
|
|
|
func (r *route) serve() error { |
|
|
|
chain, err := r.initChain() |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
for _, ns := range r.ServeNodes { |
|
|
|
node, err := gost.ParseNode(ns) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
@ -475,9 +518,24 @@ func loadConfigureFile(configureFile string) error { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
if err := json.Unmarshal(content, &options); err != nil { |
|
|
|
var cfg struct { |
|
|
|
route |
|
|
|
Routes []route |
|
|
|
} |
|
|
|
if err := json.Unmarshal(content, &cfg); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
if len(cfg.route.ServeNodes) > 0 { |
|
|
|
routes = append(routes, cfg.route) |
|
|
|
} |
|
|
|
for _, route := range cfg.Routes { |
|
|
|
if len(cfg.route.ServeNodes) > 0 { |
|
|
|
routes = append(routes, route) |
|
|
|
} |
|
|
|
} |
|
|
|
gost.Debug = cfg.Debug |
|
|
|
|
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
@ -544,16 +602,23 @@ func parseUsers(authFile string) (users []*url.Userinfo, err error) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
func parseIP(s string) (ips []string) { |
|
|
|
func parseIP(s string, port string) (ips []string) { |
|
|
|
if s == "" { |
|
|
|
return nil |
|
|
|
return |
|
|
|
} |
|
|
|
if port == "" { |
|
|
|
port = "8080" // default port
|
|
|
|
} |
|
|
|
|
|
|
|
file, err := os.Open(s) |
|
|
|
if err != nil { |
|
|
|
ss := strings.Split(s, ",") |
|
|
|
for _, s := range ss { |
|
|
|
s = strings.TrimSpace(s) |
|
|
|
if s != "" { |
|
|
|
if !strings.Contains(s, ":") { |
|
|
|
s = s + ":" + port |
|
|
|
} |
|
|
|
ips = append(ips, s) |
|
|
|
} |
|
|
|
|
|
|
|
@ -567,15 +632,20 @@ func parseIP(s string) (ips []string) { |
|
|
|
if line == "" || strings.HasPrefix(line, "#") { |
|
|
|
continue |
|
|
|
} |
|
|
|
if !strings.Contains(line, ":") { |
|
|
|
line = line + ":" + port |
|
|
|
} |
|
|
|
ips = append(ips, line) |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
type peerConfig struct { |
|
|
|
Strategy string `json:"strategy"` |
|
|
|
Filters []string `json:"filters"` |
|
|
|
Nodes []string `json:"nodes"` |
|
|
|
Strategy string `json:"strategy"` |
|
|
|
Filters []string `json:"filters"` |
|
|
|
MaxFails int `json:"max_fails"` |
|
|
|
FailTimeout int `json:"fail_timeout"` |
|
|
|
Nodes []string `json:"nodes"` |
|
|
|
} |
|
|
|
|
|
|
|
func loadPeerConfig(peer string) (config peerConfig, err error) { |
|
|
|
@ -590,13 +660,23 @@ func loadPeerConfig(peer string) (config peerConfig, err error) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
func (cfg *peerConfig) Validate() { |
|
|
|
if cfg.MaxFails <= 0 { |
|
|
|
cfg.MaxFails = 3 |
|
|
|
} |
|
|
|
if cfg.FailTimeout <= 0 { |
|
|
|
cfg.FailTimeout = 30 // seconds
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func parseStrategy(s string) gost.Strategy { |
|
|
|
switch s { |
|
|
|
case "round": |
|
|
|
return &gost.RoundStrategy{} |
|
|
|
case "random": |
|
|
|
return &gost.RandomStrategy{} |
|
|
|
case "round": |
|
|
|
fallthrough |
|
|
|
default: |
|
|
|
return &gost.RandomStrategy{} |
|
|
|
return &gost.RoundStrategy{} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|