|
|
@ -62,6 +62,16 @@ func init() { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func main() { |
|
|
func main() { |
|
|
|
|
|
// generate random self-signed certificate.
|
|
|
|
|
|
cert, err := gost.GenCertificate() |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
log.Log(err) |
|
|
|
|
|
os.Exit(1) |
|
|
|
|
|
} |
|
|
|
|
|
gost.DefaultTLSConfig = &tls.Config{ |
|
|
|
|
|
Certificates: []tls.Certificate{cert}, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
chain, err := initChain() |
|
|
chain, err := initChain() |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
log.Log(err) |
|
|
log.Log(err) |
|
|
@ -71,23 +81,56 @@ func main() { |
|
|
log.Log(err) |
|
|
log.Log(err) |
|
|
os.Exit(1) |
|
|
os.Exit(1) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
select {} |
|
|
select {} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func initChain() (*gost.Chain, error) { |
|
|
func initChain() (*gost.Chain, error) { |
|
|
chain := gost.NewChain() |
|
|
chain := gost.NewChain() |
|
|
for _, ns := range options.ChainNodes { |
|
|
for _, ns := range options.ChainNodes { |
|
|
node, err := gost.ParseNode(ns) |
|
|
// parse the base node
|
|
|
|
|
|
node, err := parseChainNode(ns) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return nil, err |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ngroup := gost.NewNodeGroup(node) |
|
|
|
|
|
|
|
|
|
|
|
// parse node peers if exists
|
|
|
|
|
|
peerCfg, err := loadPeerConfig(node.Values.Get("peer")) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
log.Log(err) |
|
|
|
|
|
} |
|
|
|
|
|
ngroup.Options = append(ngroup.Options, |
|
|
|
|
|
// gost.WithFilter(),
|
|
|
|
|
|
gost.WithStrategy(parseStrategy(peerCfg.Strategy)), |
|
|
|
|
|
) |
|
|
|
|
|
for _, s := range peerCfg.Nodes { |
|
|
|
|
|
node, err = parseChainNode(s) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return nil, err |
|
|
return nil, err |
|
|
} |
|
|
} |
|
|
|
|
|
ngroup.AddNode(node) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
chain.AddNodeGroup(ngroup) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return chain, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func parseChainNode(ns string) (node gost.Node, err error) { |
|
|
|
|
|
node, err = gost.ParseNode(ns) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
node.IPs = parseIP(node.Values.Get("ip")) |
|
|
node.IPs = parseIP(node.Values.Get("ip")) |
|
|
node.IPSelector = &gost.RoundRobinIPSelector{} |
|
|
node.IPSelector = &gost.RoundRobinIPSelector{} |
|
|
|
|
|
|
|
|
users, err := parseUsers(node.Values.Get("secrets")) |
|
|
users, err := parseUsers(node.Values.Get("secrets")) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return nil, err |
|
|
return |
|
|
} |
|
|
} |
|
|
if node.User == nil && len(users) > 0 { |
|
|
if node.User == nil && len(users) > 0 { |
|
|
node.User = users[0] |
|
|
node.User = users[0] |
|
|
@ -99,7 +142,7 @@ func initChain() (*gost.Chain, error) { |
|
|
|
|
|
|
|
|
rootCAs, err := loadCA(node.Values.Get("ca")) |
|
|
rootCAs, err := loadCA(node.Values.Get("ca")) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return nil, err |
|
|
return |
|
|
} |
|
|
} |
|
|
tlsCfg := &tls.Config{ |
|
|
tlsCfg := &tls.Config{ |
|
|
ServerName: serverName, |
|
|
ServerName: serverName, |
|
|
@ -127,12 +170,14 @@ func initChain() (*gost.Chain, error) { |
|
|
case "mwss": |
|
|
case "mwss": |
|
|
tr = gost.MWSSTransporter(wsOpts) |
|
|
tr = gost.MWSSTransporter(wsOpts) |
|
|
case "kcp": |
|
|
case "kcp": |
|
|
|
|
|
/* |
|
|
if !chain.IsEmpty() { |
|
|
if !chain.IsEmpty() { |
|
|
return nil, errors.New("KCP must be the first node in the proxy chain") |
|
|
return nil, errors.New("KCP must be the first node in the proxy chain") |
|
|
} |
|
|
} |
|
|
|
|
|
*/ |
|
|
config, err := parseKCPConfig(node.Values.Get("c")) |
|
|
config, err := parseKCPConfig(node.Values.Get("c")) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return nil, err |
|
|
return node, err |
|
|
} |
|
|
} |
|
|
tr = gost.KCPTransporter(config) |
|
|
tr = gost.KCPTransporter(config) |
|
|
case "ssh": |
|
|
case "ssh": |
|
|
@ -142,9 +187,11 @@ func initChain() (*gost.Chain, error) { |
|
|
tr = gost.SSHTunnelTransporter() |
|
|
tr = gost.SSHTunnelTransporter() |
|
|
} |
|
|
} |
|
|
case "quic": |
|
|
case "quic": |
|
|
|
|
|
/* |
|
|
if !chain.IsEmpty() { |
|
|
if !chain.IsEmpty() { |
|
|
return nil, errors.New("QUIC must be the first node in the proxy chain") |
|
|
return nil, errors.New("QUIC must be the first node in the proxy chain") |
|
|
} |
|
|
} |
|
|
|
|
|
*/ |
|
|
config := &gost.QUICConfig{ |
|
|
config := &gost.QUICConfig{ |
|
|
TLSConfig: tlsCfg, |
|
|
TLSConfig: tlsCfg, |
|
|
KeepAlive: toBool(node.Values.Get("keepalive")), |
|
|
KeepAlive: toBool(node.Values.Get("keepalive")), |
|
|
@ -159,7 +206,7 @@ func initChain() (*gost.Chain, error) { |
|
|
|
|
|
|
|
|
case "obfs4": |
|
|
case "obfs4": |
|
|
if err := gost.Obfs4Init(node, false); err != nil { |
|
|
if err := gost.Obfs4Init(node, false); err != nil { |
|
|
return nil, err |
|
|
return node, err |
|
|
} |
|
|
} |
|
|
tr = gost.Obfs4Transporter() |
|
|
tr = gost.Obfs4Transporter() |
|
|
case "ohttp": |
|
|
case "ohttp": |
|
|
@ -168,13 +215,6 @@ func initChain() (*gost.Chain, error) { |
|
|
tr = gost.TCPTransporter() |
|
|
tr = gost.TCPTransporter() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if tr.Multiplex() { |
|
|
|
|
|
node.DialOptions = append(node.DialOptions, |
|
|
|
|
|
gost.ChainDialOption(chain), |
|
|
|
|
|
) |
|
|
|
|
|
chain = gost.NewChain() // cutoff the chain for multiplex
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var connector gost.Connector |
|
|
var connector gost.Connector |
|
|
switch node.Protocol { |
|
|
switch node.Protocol { |
|
|
case "http2": |
|
|
case "http2": |
|
|
@ -221,10 +261,8 @@ func initChain() (*gost.Chain, error) { |
|
|
Connector: connector, |
|
|
Connector: connector, |
|
|
Transporter: tr, |
|
|
Transporter: tr, |
|
|
} |
|
|
} |
|
|
chain.AddNode(node) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return chain, nil |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func serve(chain *gost.Chain) error { |
|
|
func serve(chain *gost.Chain) error { |
|
|
@ -533,3 +571,32 @@ func parseIP(s string) (ips []string) { |
|
|
} |
|
|
} |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type peerConfig struct { |
|
|
|
|
|
Strategy string `json:"strategy"` |
|
|
|
|
|
Filters []string `json:"filters"` |
|
|
|
|
|
Nodes []string `json:"nodes"` |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func loadPeerConfig(peer string) (config peerConfig, err error) { |
|
|
|
|
|
if peer == "" { |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
content, err := ioutil.ReadFile(peer) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
err = json.Unmarshal(content, &config) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func parseStrategy(s string) gost.Strategy { |
|
|
|
|
|
switch s { |
|
|
|
|
|
case "round": |
|
|
|
|
|
return &gost.RoundStrategy{} |
|
|
|
|
|
case "random": |
|
|
|
|
|
fallthrough |
|
|
|
|
|
default: |
|
|
|
|
|
return &gost.RandomStrategy{} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|