mirror of https://github.com/ginuerzh/gost
6 changed files with 515 additions and 444 deletions
@ -0,0 +1,412 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"crypto/sha256" |
||||
|
"crypto/tls" |
||||
|
"net" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/ginuerzh/gost" |
||||
|
) |
||||
|
|
||||
|
type route struct { |
||||
|
ServeNodes stringList |
||||
|
ChainNodes stringList |
||||
|
Retries int |
||||
|
server *gost.Server |
||||
|
} |
||||
|
|
||||
|
func (r *route) initChain() (*gost.Chain, error) { |
||||
|
chain := gost.NewChain() |
||||
|
chain.Retries = r.Retries |
||||
|
gid := 1 // group ID
|
||||
|
|
||||
|
for _, ns := range r.ChainNodes { |
||||
|
ngroup := gost.NewNodeGroup() |
||||
|
ngroup.ID = gid |
||||
|
gid++ |
||||
|
|
||||
|
// parse the base nodes
|
||||
|
nodes, err := parseChainNode(ns) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
nid := 1 // node ID
|
||||
|
for i := range nodes { |
||||
|
nodes[i].ID = nid |
||||
|
nid++ |
||||
|
} |
||||
|
ngroup.AddNode(nodes...) |
||||
|
|
||||
|
ngroup.SetSelector(nil, |
||||
|
gost.WithFilter(&gost.FailFilter{ |
||||
|
MaxFails: defaultMaxFails, |
||||
|
FailTimeout: defaultFailTimeout, |
||||
|
}), |
||||
|
gost.WithStrategy(parseStrategy(nodes[0].Get("strategy"))), |
||||
|
) |
||||
|
|
||||
|
go gost.PeriodReload(&peerConfig{ |
||||
|
group: ngroup, |
||||
|
baseNodes: nodes, |
||||
|
}, nodes[0].Get("peer")) |
||||
|
|
||||
|
chain.AddNodeGroup(ngroup) |
||||
|
} |
||||
|
|
||||
|
return chain, nil |
||||
|
} |
||||
|
|
||||
|
func parseChainNode(ns string) (nodes []gost.Node, err error) { |
||||
|
node, err := gost.ParseNode(ns) |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
users, err := parseUsers(node.Get("secrets")) |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
if node.User == nil && len(users) > 0 { |
||||
|
node.User = users[0] |
||||
|
} |
||||
|
serverName, sport, _ := net.SplitHostPort(node.Addr) |
||||
|
if serverName == "" { |
||||
|
serverName = "localhost" // default server name
|
||||
|
} |
||||
|
|
||||
|
rootCAs, err := loadCA(node.Get("ca")) |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
tlsCfg := &tls.Config{ |
||||
|
ServerName: serverName, |
||||
|
InsecureSkipVerify: !node.GetBool("secure"), |
||||
|
RootCAs: rootCAs, |
||||
|
} |
||||
|
wsOpts := &gost.WSOptions{} |
||||
|
wsOpts.EnableCompression = node.GetBool("compression") |
||||
|
wsOpts.ReadBufferSize = node.GetInt("rbuf") |
||||
|
wsOpts.WriteBufferSize = node.GetInt("wbuf") |
||||
|
wsOpts.UserAgent = node.Get("agent") |
||||
|
|
||||
|
var tr gost.Transporter |
||||
|
switch node.Transport { |
||||
|
case "tls": |
||||
|
tr = gost.TLSTransporter() |
||||
|
case "mtls": |
||||
|
tr = gost.MTLSTransporter() |
||||
|
case "ws": |
||||
|
tr = gost.WSTransporter(wsOpts) |
||||
|
case "mws": |
||||
|
tr = gost.MWSTransporter(wsOpts) |
||||
|
case "wss": |
||||
|
tr = gost.WSSTransporter(wsOpts) |
||||
|
case "mwss": |
||||
|
tr = gost.MWSSTransporter(wsOpts) |
||||
|
case "kcp": |
||||
|
config, err := parseKCPConfig(node.Get("c")) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
tr = gost.KCPTransporter(config) |
||||
|
case "ssh": |
||||
|
if node.Protocol == "direct" || node.Protocol == "remote" { |
||||
|
tr = gost.SSHForwardTransporter() |
||||
|
} else { |
||||
|
tr = gost.SSHTunnelTransporter() |
||||
|
} |
||||
|
case "quic": |
||||
|
config := &gost.QUICConfig{ |
||||
|
TLSConfig: tlsCfg, |
||||
|
KeepAlive: node.GetBool("keepalive"), |
||||
|
Timeout: time.Duration(node.GetInt("timeout")) * time.Second, |
||||
|
IdleTimeout: time.Duration(node.GetInt("idle")) * time.Second, |
||||
|
} |
||||
|
|
||||
|
if cipher := node.Get("cipher"); cipher != "" { |
||||
|
sum := sha256.Sum256([]byte(cipher)) |
||||
|
config.Key = sum[:] |
||||
|
} |
||||
|
|
||||
|
tr = gost.QUICTransporter(config) |
||||
|
case "http2": |
||||
|
tr = gost.HTTP2Transporter(tlsCfg) |
||||
|
case "h2": |
||||
|
tr = gost.H2Transporter(tlsCfg) |
||||
|
case "h2c": |
||||
|
tr = gost.H2CTransporter() |
||||
|
|
||||
|
case "obfs4": |
||||
|
tr = gost.Obfs4Transporter() |
||||
|
case "ohttp": |
||||
|
tr = gost.ObfsHTTPTransporter() |
||||
|
default: |
||||
|
tr = gost.TCPTransporter() |
||||
|
} |
||||
|
|
||||
|
var connector gost.Connector |
||||
|
switch node.Protocol { |
||||
|
case "http2": |
||||
|
connector = gost.HTTP2Connector(node.User) |
||||
|
case "socks", "socks5": |
||||
|
connector = gost.SOCKS5Connector(node.User) |
||||
|
case "socks4": |
||||
|
connector = gost.SOCKS4Connector() |
||||
|
case "socks4a": |
||||
|
connector = gost.SOCKS4AConnector() |
||||
|
case "ss": |
||||
|
connector = gost.ShadowConnector(node.User) |
||||
|
case "direct": |
||||
|
connector = gost.SSHDirectForwardConnector() |
||||
|
case "remote": |
||||
|
connector = gost.SSHRemoteForwardConnector() |
||||
|
case "forward": |
||||
|
connector = gost.ForwardConnector() |
||||
|
case "sni": |
||||
|
connector = gost.SNIConnector(node.Get("host")) |
||||
|
case "http": |
||||
|
fallthrough |
||||
|
default: |
||||
|
node.Protocol = "http" // default protocol is HTTP
|
||||
|
connector = gost.HTTPConnector(node.User) |
||||
|
} |
||||
|
|
||||
|
timeout := node.GetInt("timeout") |
||||
|
node.DialOptions = append(node.DialOptions, |
||||
|
gost.TimeoutDialOption(time.Duration(timeout)*time.Second), |
||||
|
) |
||||
|
|
||||
|
handshakeOptions := []gost.HandshakeOption{ |
||||
|
gost.AddrHandshakeOption(node.Addr), |
||||
|
gost.HostHandshakeOption(node.Host), |
||||
|
gost.UserHandshakeOption(node.User), |
||||
|
gost.TLSConfigHandshakeOption(tlsCfg), |
||||
|
gost.IntervalHandshakeOption(time.Duration(node.GetInt("ping")) * time.Second), |
||||
|
gost.TimeoutHandshakeOption(time.Duration(timeout) * time.Second), |
||||
|
gost.RetryHandshakeOption(node.GetInt("retry")), |
||||
|
} |
||||
|
node.Client = &gost.Client{ |
||||
|
Connector: connector, |
||||
|
Transporter: tr, |
||||
|
} |
||||
|
|
||||
|
node.Bypass = parseBypass(node.Get("bypass")) |
||||
|
|
||||
|
ips := parseIP(node.Get("ip"), sport) |
||||
|
for _, ip := range ips { |
||||
|
nd := node.Clone() |
||||
|
nd.Addr = ip |
||||
|
// override the default node address
|
||||
|
nd.HandshakeOptions = append(handshakeOptions, gost.AddrHandshakeOption(ip)) |
||||
|
// One node per IP
|
||||
|
nodes = append(nodes, nd) |
||||
|
} |
||||
|
if len(ips) == 0 { |
||||
|
node.HandshakeOptions = handshakeOptions |
||||
|
nodes = []gost.Node{node} |
||||
|
} |
||||
|
|
||||
|
if node.Transport == "obfs4" { |
||||
|
for i := range nodes { |
||||
|
if err := gost.Obfs4Init(nodes[i], false); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return |
||||
|
} |
||||
|
|
||||
|
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 |
||||
|
} |
||||
|
users, err := parseUsers(node.Get("secrets")) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
if node.User != nil { |
||||
|
users = append(users, node.User) |
||||
|
} |
||||
|
certFile, keyFile := node.Get("cert"), node.Get("key") |
||||
|
tlsCfg, err := tlsConfig(certFile, keyFile) |
||||
|
if err != nil && certFile != "" && keyFile != "" { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
wsOpts := &gost.WSOptions{} |
||||
|
wsOpts.EnableCompression = node.GetBool("compression") |
||||
|
wsOpts.ReadBufferSize = node.GetInt("rbuf") |
||||
|
wsOpts.WriteBufferSize = node.GetInt("wbuf") |
||||
|
|
||||
|
var ln gost.Listener |
||||
|
switch node.Transport { |
||||
|
case "tls": |
||||
|
ln, err = gost.TLSListener(node.Addr, tlsCfg) |
||||
|
case "mtls": |
||||
|
ln, err = gost.MTLSListener(node.Addr, tlsCfg) |
||||
|
case "ws": |
||||
|
wsOpts.WriteBufferSize = node.GetInt("wbuf") |
||||
|
ln, err = gost.WSListener(node.Addr, wsOpts) |
||||
|
case "mws": |
||||
|
ln, err = gost.MWSListener(node.Addr, wsOpts) |
||||
|
case "wss": |
||||
|
ln, err = gost.WSSListener(node.Addr, tlsCfg, wsOpts) |
||||
|
case "mwss": |
||||
|
ln, err = gost.MWSSListener(node.Addr, tlsCfg, wsOpts) |
||||
|
case "kcp": |
||||
|
config, er := parseKCPConfig(node.Get("c")) |
||||
|
if er != nil { |
||||
|
return er |
||||
|
} |
||||
|
ln, err = gost.KCPListener(node.Addr, config) |
||||
|
case "ssh": |
||||
|
config := &gost.SSHConfig{ |
||||
|
Users: users, |
||||
|
TLSConfig: tlsCfg, |
||||
|
} |
||||
|
if node.Protocol == "forward" { |
||||
|
ln, err = gost.TCPListener(node.Addr) |
||||
|
} else { |
||||
|
ln, err = gost.SSHTunnelListener(node.Addr, config) |
||||
|
} |
||||
|
case "quic": |
||||
|
config := &gost.QUICConfig{ |
||||
|
TLSConfig: tlsCfg, |
||||
|
KeepAlive: node.GetBool("keepalive"), |
||||
|
Timeout: time.Duration(node.GetInt("timeout")) * time.Second, |
||||
|
IdleTimeout: time.Duration(node.GetInt("idle")) * time.Second, |
||||
|
} |
||||
|
if cipher := node.Get("cipher"); cipher != "" { |
||||
|
sum := sha256.Sum256([]byte(cipher)) |
||||
|
config.Key = sum[:] |
||||
|
} |
||||
|
|
||||
|
ln, err = gost.QUICListener(node.Addr, config) |
||||
|
case "http2": |
||||
|
ln, err = gost.HTTP2Listener(node.Addr, tlsCfg) |
||||
|
case "h2": |
||||
|
ln, err = gost.H2Listener(node.Addr, tlsCfg) |
||||
|
case "h2c": |
||||
|
ln, err = gost.H2CListener(node.Addr) |
||||
|
case "tcp": |
||||
|
// Directly use SSH port forwarding if the last chain node is forward+ssh
|
||||
|
if chain.LastNode().Protocol == "forward" && chain.LastNode().Transport == "ssh" { |
||||
|
chain.Nodes()[len(chain.Nodes())-1].Client.Connector = gost.SSHDirectForwardConnector() |
||||
|
chain.Nodes()[len(chain.Nodes())-1].Client.Transporter = gost.SSHForwardTransporter() |
||||
|
} |
||||
|
ln, err = gost.TCPListener(node.Addr) |
||||
|
case "rtcp": |
||||
|
// Directly use SSH port forwarding if the last chain node is forward+ssh
|
||||
|
if chain.LastNode().Protocol == "forward" && chain.LastNode().Transport == "ssh" { |
||||
|
chain.Nodes()[len(chain.Nodes())-1].Client.Connector = gost.SSHRemoteForwardConnector() |
||||
|
chain.Nodes()[len(chain.Nodes())-1].Client.Transporter = gost.SSHForwardTransporter() |
||||
|
} |
||||
|
ln, err = gost.TCPRemoteForwardListener(node.Addr, chain) |
||||
|
case "udp": |
||||
|
ln, err = gost.UDPDirectForwardListener(node.Addr, time.Duration(node.GetInt("ttl"))*time.Second) |
||||
|
case "rudp": |
||||
|
ln, err = gost.UDPRemoteForwardListener(node.Addr, chain, time.Duration(node.GetInt("ttl"))*time.Second) |
||||
|
case "ssu": |
||||
|
ln, err = gost.ShadowUDPListener(node.Addr, node.User, time.Duration(node.GetInt("ttl"))*time.Second) |
||||
|
case "obfs4": |
||||
|
if err = gost.Obfs4Init(node, true); err != nil { |
||||
|
return err |
||||
|
} |
||||
|
ln, err = gost.Obfs4Listener(node.Addr) |
||||
|
case "ohttp": |
||||
|
ln, err = gost.ObfsHTTPListener(node.Addr) |
||||
|
default: |
||||
|
ln, err = gost.TCPListener(node.Addr) |
||||
|
} |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
var handler gost.Handler |
||||
|
switch node.Protocol { |
||||
|
case "http2": |
||||
|
handler = gost.HTTP2Handler() |
||||
|
case "socks", "socks5": |
||||
|
handler = gost.SOCKS5Handler() |
||||
|
case "socks4", "socks4a": |
||||
|
handler = gost.SOCKS4Handler() |
||||
|
case "ss": |
||||
|
handler = gost.ShadowHandler() |
||||
|
case "http": |
||||
|
handler = gost.HTTPHandler() |
||||
|
case "tcp": |
||||
|
handler = gost.TCPDirectForwardHandler(node.Remote) |
||||
|
case "rtcp": |
||||
|
handler = gost.TCPRemoteForwardHandler(node.Remote) |
||||
|
case "udp": |
||||
|
handler = gost.UDPDirectForwardHandler(node.Remote) |
||||
|
case "rudp": |
||||
|
handler = gost.UDPRemoteForwardHandler(node.Remote) |
||||
|
case "forward": |
||||
|
handler = gost.SSHForwardHandler() |
||||
|
case "redirect": |
||||
|
handler = gost.TCPRedirectHandler() |
||||
|
case "ssu": |
||||
|
handler = gost.ShadowUDPdHandler() |
||||
|
case "sni": |
||||
|
handler = gost.SNIHandler() |
||||
|
default: |
||||
|
// start from 2.5, if remote is not empty, then we assume that it is a forward tunnel.
|
||||
|
if node.Remote != "" { |
||||
|
handler = gost.TCPDirectForwardHandler(node.Remote) |
||||
|
} else { |
||||
|
handler = gost.AutoHandler() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var whitelist, blacklist *gost.Permissions |
||||
|
if node.Values.Get("whitelist") != "" { |
||||
|
if whitelist, err = gost.ParsePermissions(node.Get("whitelist")); err != nil { |
||||
|
return err |
||||
|
} |
||||
|
} |
||||
|
if node.Values.Get("blacklist") != "" { |
||||
|
if blacklist, err = gost.ParsePermissions(node.Get("blacklist")); err != nil { |
||||
|
return err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
handler.Init( |
||||
|
gost.AddrHandlerOption(node.Addr), |
||||
|
gost.ChainHandlerOption(chain), |
||||
|
gost.UsersHandlerOption(users...), |
||||
|
gost.TLSConfigHandlerOption(tlsCfg), |
||||
|
gost.WhitelistHandlerOption(whitelist), |
||||
|
gost.BlacklistHandlerOption(blacklist), |
||||
|
gost.StrategyHandlerOption(parseStrategy(node.Get("strategy"))), |
||||
|
gost.BypassHandlerOption(parseBypass(node.Get("bypass"))), |
||||
|
gost.ResolverHandlerOption(parseResolver(node.Get("dns"))), |
||||
|
gost.HostsHandlerOption(parseHosts(node.Get("hosts"))), |
||||
|
gost.RetryHandlerOption(node.GetInt("retry")), |
||||
|
gost.TimeoutHandlerOption(time.Duration(node.GetInt("timeout"))*time.Second), |
||||
|
gost.ProbeResistHandlerOption(node.Get("probe_resist")), |
||||
|
) |
||||
|
|
||||
|
r.server = &gost.Server{Listener: ln} |
||||
|
go r.server.Serve(handler) |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (r *route) Close() error { |
||||
|
if r == nil || r.server == nil { |
||||
|
return nil |
||||
|
} |
||||
|
return r.server.Close() |
||||
|
} |
||||
Loading…
Reference in new issue