add bypass support for proxy chain
This commit is contained in:
parent
f6e09eaae6
commit
d39e211f3e
61
chain.go
61
chain.go
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/go-log/log"
|
"github.com/go-log/log"
|
||||||
)
|
)
|
||||||
@ -100,11 +101,7 @@ func (c *Chain) IsEmpty() bool {
|
|||||||
// Dial connects to the target address addr through the chain.
|
// Dial connects to the target address addr through the chain.
|
||||||
// If the chain is empty, it will use the net.Dial directly.
|
// If the chain is empty, it will use the net.Dial directly.
|
||||||
func (c *Chain) Dial(addr string) (conn net.Conn, err error) {
|
func (c *Chain) Dial(addr string) (conn net.Conn, err error) {
|
||||||
if c.IsEmpty() {
|
for i := 0; i < c.Retries; i++ {
|
||||||
return net.DialTimeout("tcp", addr, DialTimeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < c.Retries+1; i++ {
|
|
||||||
conn, err = c.dial(addr)
|
conn, err = c.dial(addr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
@ -114,10 +111,13 @@ func (c *Chain) Dial(addr string) (conn net.Conn, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Chain) dial(addr string) (net.Conn, error) {
|
func (c *Chain) dial(addr string) (net.Conn, error) {
|
||||||
route, err := c.selectRoute()
|
route, err := c.selectRouteFor(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if route.IsEmpty() {
|
||||||
|
return net.DialTimeout("tcp", addr, DialTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
conn, err := route.getConn()
|
conn, err := route.getConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -135,7 +135,7 @@ func (c *Chain) dial(addr string) (net.Conn, error) {
|
|||||||
// Conn obtains a handshaked connection to the last node of the chain.
|
// Conn obtains a handshaked connection to the last node of the chain.
|
||||||
// If the chain is empty, it returns an ErrEmptyChain error.
|
// If the chain is empty, it returns an ErrEmptyChain error.
|
||||||
func (c *Chain) Conn() (conn net.Conn, err error) {
|
func (c *Chain) Conn() (conn net.Conn, err error) {
|
||||||
for i := 0; i < c.Retries+1; i++ {
|
for i := 0; i < c.Retries; i++ {
|
||||||
var route *Chain
|
var route *Chain
|
||||||
route, err = c.selectRoute()
|
route, err = c.selectRoute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -228,3 +228,50 @@ func (c *Chain) selectRoute() (route *Chain, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// selectRouteFor selects route with bypass testing.
|
||||||
|
func (c *Chain) selectRouteFor(addr string) (route *Chain, err error) {
|
||||||
|
if c.IsEmpty() || c.isRoute {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
route = newRoute()
|
||||||
|
route.Retries = c.Retries
|
||||||
|
|
||||||
|
for _, group := range c.nodeGroups {
|
||||||
|
var node Node
|
||||||
|
node, err = group.Next()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: IPv6 will not work.
|
||||||
|
if strings.Contains(addr, ":") {
|
||||||
|
addr = strings.Split(addr, ":")[0]
|
||||||
|
}
|
||||||
|
if node.Bypass.Contains(addr) {
|
||||||
|
if Debug {
|
||||||
|
buf.WriteString(fmt.Sprintf("[%d@bypass: %s]", node.ID, addr))
|
||||||
|
log.Log("select route:", buf.String())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(fmt.Sprintf("%s -> ", node.String()))
|
||||||
|
|
||||||
|
if node.Client.Transporter.Multiplex() {
|
||||||
|
node.DialOptions = append(node.DialOptions,
|
||||||
|
ChainDialOption(route),
|
||||||
|
)
|
||||||
|
route = newRoute() // cutoff the chain for multiplex.
|
||||||
|
route.Retries = c.Retries
|
||||||
|
}
|
||||||
|
|
||||||
|
route.AddNode(node)
|
||||||
|
}
|
||||||
|
if Debug {
|
||||||
|
log.Log("select route:", buf.String())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
5
cmd/gost/bypass.txt
Normal file
5
cmd/gost/bypass.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
10.0.0.1
|
||||||
|
192.168.0.0/24
|
||||||
|
172.1.0.0/16
|
||||||
|
192.168.100.190/32
|
||||||
|
*.example.com
|
@ -92,7 +92,11 @@ type route struct {
|
|||||||
|
|
||||||
func (r *route) initChain() (*gost.Chain, error) {
|
func (r *route) initChain() (*gost.Chain, error) {
|
||||||
chain := gost.NewChain()
|
chain := gost.NewChain()
|
||||||
|
|
||||||
chain.Retries = r.Retries
|
chain.Retries = r.Retries
|
||||||
|
if chain.Retries == 0 {
|
||||||
|
chain.Retries = 1
|
||||||
|
}
|
||||||
|
|
||||||
gid := 1 // group ID
|
gid := 1 // group ID
|
||||||
|
|
||||||
@ -143,6 +147,17 @@ func (r *route) initChain() (*gost.Chain, error) {
|
|||||||
ngroup.AddNode(nodes...)
|
ngroup.AddNode(nodes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bypass *gost.Bypass
|
||||||
|
if peerCfg.Bypass != nil {
|
||||||
|
bypass = gost.NewBypassPatterns(peerCfg.Bypass.Patterns, peerCfg.Bypass.Reverse)
|
||||||
|
}
|
||||||
|
nodes = ngroup.Nodes()
|
||||||
|
for i := range nodes {
|
||||||
|
if nodes[i].Bypass == nil {
|
||||||
|
nodes[i].Bypass = bypass // use global bypass if local bypass does not exist.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
chain.AddNodeGroup(ngroup)
|
chain.AddNodeGroup(ngroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,9 +312,12 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
|
|||||||
Transporter: tr,
|
Transporter: tr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node.Bypass = parseBypass(node.Get("bypass"))
|
||||||
|
|
||||||
ips := parseIP(node.Get("ip"), sport)
|
ips := parseIP(node.Get("ip"), sport)
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
node.Addr = ip
|
node.Addr = ip
|
||||||
|
// override the default node address
|
||||||
node.HandshakeOptions = append(handshakeOptions, gost.AddrHandshakeOption(ip))
|
node.HandshakeOptions = append(handshakeOptions, gost.AddrHandshakeOption(ip))
|
||||||
nodes = append(nodes, node)
|
nodes = append(nodes, node)
|
||||||
}
|
}
|
||||||
@ -484,14 +502,9 @@ func (r *route) serve() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fBypass := node.Get("bypass")
|
|
||||||
if fBypass == "" {
|
|
||||||
fBypass = "bypass" // default bypass file
|
|
||||||
}
|
|
||||||
|
|
||||||
srv := &gost.Server{Listener: ln}
|
srv := &gost.Server{Listener: ln}
|
||||||
srv.Init(
|
srv.Init(
|
||||||
gost.BypassServerOption(parseBypass(fBypass)),
|
gost.BypassServerOption(parseBypass(node.Get("bypass"))),
|
||||||
)
|
)
|
||||||
go srv.Serve(handler)
|
go srv.Serve(handler)
|
||||||
}
|
}
|
||||||
@ -657,6 +670,12 @@ type peerConfig struct {
|
|||||||
MaxFails int `json:"max_fails"`
|
MaxFails int `json:"max_fails"`
|
||||||
FailTimeout int `json:"fail_timeout"`
|
FailTimeout int `json:"fail_timeout"`
|
||||||
Nodes []string `json:"nodes"`
|
Nodes []string `json:"nodes"`
|
||||||
|
Bypass *bypass `json:"bypass"` // global bypass
|
||||||
|
}
|
||||||
|
|
||||||
|
type bypass struct {
|
||||||
|
Reverse bool `json:"reverse"`
|
||||||
|
Patterns []string `json:"patterns"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadPeerConfig(peer string) (config peerConfig, err error) {
|
func loadPeerConfig(peer string) (config peerConfig, err error) {
|
||||||
@ -694,16 +713,29 @@ func parseStrategy(s string) gost.Strategy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseBypass(fpath string) (bypass *gost.Bypass) {
|
func parseBypass(s string) *gost.Bypass {
|
||||||
if fpath == "" {
|
if s == "" {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
f, err := os.Open(fpath)
|
var matchers []gost.Matcher
|
||||||
if err != nil {
|
var reversed bool
|
||||||
return
|
if strings.HasPrefix(s, "~") {
|
||||||
|
reversed = true
|
||||||
|
s = strings.TrimLeft(s, "~")
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(s)
|
||||||
|
if err != nil {
|
||||||
|
for _, s := range strings.Split(s, ",") {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if s == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matchers = append(matchers, gost.NewMatcher(s))
|
||||||
|
}
|
||||||
|
return gost.NewBypass(matchers, reversed)
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchers []gost.Matcher
|
|
||||||
scanner := bufio.NewScanner(f)
|
scanner := bufio.NewScanner(f)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
@ -716,6 +748,5 @@ func parseBypass(fpath string) (bypass *gost.Bypass) {
|
|||||||
}
|
}
|
||||||
matchers = append(matchers, gost.NewMatcher(line))
|
matchers = append(matchers, gost.NewMatcher(line))
|
||||||
}
|
}
|
||||||
bypass = gost.NewBypass(matchers, strings.HasPrefix(fpath, "~"))
|
return gost.NewBypass(matchers, reversed)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
@ -6,5 +6,13 @@
|
|||||||
"socks5://:1081",
|
"socks5://:1081",
|
||||||
"socks://:1082",
|
"socks://:1082",
|
||||||
"socks4a://:1083"
|
"socks4a://:1083"
|
||||||
|
],
|
||||||
|
"bypass":{
|
||||||
|
"reverse": false,
|
||||||
|
"patterns": [
|
||||||
|
"10.0.0.1",
|
||||||
|
"192.168.0.0/24",
|
||||||
|
"*.example.com"
|
||||||
]
|
]
|
||||||
|
}
|
||||||
}
|
}
|
19
http.go
19
http.go
@ -150,10 +150,15 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
route, err := h.options.Chain.selectRouteFor(req.Host)
|
||||||
|
if err != nil {
|
||||||
|
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
// forward http request
|
// forward http request
|
||||||
lastNode := h.options.Chain.LastNode()
|
lastNode := route.LastNode()
|
||||||
if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
|
if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
|
||||||
h.forwardRequest(conn, req)
|
h.forwardRequest(conn, req, route)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +167,7 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
|
|||||||
host += ":80"
|
host += ":80"
|
||||||
}
|
}
|
||||||
|
|
||||||
cc, err := h.options.Chain.Dial(host)
|
cc, err := route.Dial(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err)
|
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err)
|
||||||
|
|
||||||
@ -197,13 +202,13 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
|
|||||||
log.Logf("[http] %s >-< %s", cc.LocalAddr(), host)
|
log.Logf("[http] %s >-< %s", cc.LocalAddr(), host)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request) {
|
func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request, route *Chain) {
|
||||||
if h.options.Chain.IsEmpty() {
|
if route.IsEmpty() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lastNode := h.options.Chain.LastNode()
|
lastNode := route.LastNode()
|
||||||
|
|
||||||
cc, err := h.options.Chain.Conn()
|
cc, err := route.Conn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), lastNode.Addr, err)
|
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), lastNode.Addr, err)
|
||||||
|
|
||||||
|
1
node.go
1
node.go
@ -25,6 +25,7 @@ type Node struct {
|
|||||||
group *NodeGroup
|
group *NodeGroup
|
||||||
failCount uint32
|
failCount uint32
|
||||||
failTime int64
|
failTime int64
|
||||||
|
Bypass *Bypass
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseNode parses the node info.
|
// ParseNode parses the node info.
|
||||||
|
2
socks.go
2
socks.go
@ -267,7 +267,7 @@ func (c *socks4Connector) Connect(conn net.Conn, addr string) (net.Conn, error)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(taddr.IP) == 0 {
|
if len(taddr.IP) == 0 {
|
||||||
taddr.IP = net.IPv4(0, 0, 0, 0)
|
taddr.IP = net.IPv4zero
|
||||||
}
|
}
|
||||||
|
|
||||||
req := gosocks4.NewRequest(gosocks4.CmdConnect,
|
req := gosocks4.NewRequest(gosocks4.CmdConnect,
|
||||||
|
Loading…
Reference in New Issue
Block a user