1. optimized strategy proxy code. 2. changed config name "checkhost" -> "checkwebsite" 3. optimized proxy check function.

This commit is contained in:
nadoo 2017-07-29 21:31:01 +08:00
parent 1ba0f38c9e
commit 88dda17725
10 changed files with 93 additions and 90 deletions

View File

@ -24,7 +24,7 @@ const TCPDNSHEADERLen = 2 + UDPDNSHeaderLen
const MaxUDPDNSLen = 512 const MaxUDPDNSLen = 512
type dnstun struct { type dnstun struct {
Proxy *proxy
addr string addr string
raddr string raddr string
} }
@ -32,7 +32,7 @@ type dnstun struct {
// DNSTunProxy returns a dns forwarder. client -> dns.udp -> glider -> forwarder -> remote dns addr // DNSTunProxy returns a dns forwarder. client -> dns.udp -> glider -> forwarder -> remote dns addr
func DNSTunProxy(addr, raddr string, upProxy Proxy) (Proxy, error) { func DNSTunProxy(addr, raddr string, upProxy Proxy) (Proxy, error) {
s := &dnstun{ s := &dnstun{
Proxy: upProxy, proxy: newProxy(addr, upProxy),
addr: addr, addr: addr,
raddr: raddr, raddr: raddr,
} }

View File

@ -89,7 +89,8 @@ strategy=rr
# Used to connect via forwarders, if the host is unreachable, the forwarder # Used to connect via forwarders, if the host is unreachable, the forwarder
# will be set to disabled. # will be set to disabled.
checkhost=www.apple.com:443 # MUST BE A HTTP or HTTPS WEBSITE HOST ADDRESS
checkwebsite=www.apple.com:443
# check duration(seconds) # check duration(seconds)
checkduration=30 checkduration=30

View File

@ -18,14 +18,14 @@ import (
// httpproxy // httpproxy
type httpproxy struct { type httpproxy struct {
Proxy *proxy
addr string addr string
} }
// HTTPProxy returns a http proxy. // HTTPProxy returns a http proxy.
func HTTPProxy(addr string, upProxy Proxy) (Proxy, error) { func HTTPProxy(addr string, upProxy Proxy) (Proxy, error) {
s := &httpproxy{ s := &httpproxy{
Proxy: upProxy, proxy: newProxy(addr, upProxy),
addr: addr, addr: addr,
} }
@ -56,7 +56,6 @@ func (s *httpproxy) ListenAndServe() {
// Serve . // Serve .
func (s *httpproxy) Serve(c net.Conn) { func (s *httpproxy) Serve(c net.Conn) {
defer c.Close() defer c.Close()
if c, ok := c.(*net.TCPConn); ok { if c, ok := c.(*net.TCPConn); ok {

11
main.go
View File

@ -12,12 +12,12 @@ import (
) )
// VERSION . // VERSION .
const VERSION = "0.2.1" const VERSION = "0.3"
var conf struct { var conf struct {
Verbose bool Verbose bool
Strategy string Strategy string
CheckHost string CheckWebSite string
CheckDuration int CheckDuration int
Listen arrFlags Listen arrFlags
Forward arrFlags Forward arrFlags
@ -121,7 +121,7 @@ func main() {
flag.BoolVar(&conf.Verbose, "verbose", false, "verbose mode") flag.BoolVar(&conf.Verbose, "verbose", false, "verbose mode")
flag.StringVar(&conf.Strategy, "strategy", "rr", "forward strategy, default: rr") flag.StringVar(&conf.Strategy, "strategy", "rr", "forward strategy, default: rr")
flag.StringVar(&conf.CheckHost, "checkhost", "www.apple.com:443", "proxy check address") flag.StringVar(&conf.CheckWebSite, "checkwebsite", "www.apple.com:443", "proxy check WEBSITE address")
flag.IntVar(&conf.CheckDuration, "checkduration", 30, "proxy check duration(seconds)") flag.IntVar(&conf.CheckDuration, "checkduration", 30, "proxy check duration(seconds)")
flag.Var(&conf.Listen, "listen", "listen url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT") flag.Var(&conf.Listen, "listen", "listen url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT")
flag.Var(&conf.Forward, "forward", "forward url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT[,SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT]") flag.Var(&conf.Forward, "forward", "forward url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT[,SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT]")
@ -152,8 +152,9 @@ func main() {
forwarders = append(forwarders, forward) forwarders = append(forwarders, forward)
} }
forwarder := newStrategyForwarder(conf.Strategy, forwarders)
for _, listen := range conf.Listen { for _, listen := range conf.Listen {
local, err := ProxyFromURL(listen, forwarders...) local, err := ProxyFromURL(listen, forwarder)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -163,7 +164,7 @@ func main() {
if len(forwarders) > 1 { if len(forwarders) > 1 {
for _, forward := range forwarders { for _, forward := range forwarders {
go check(forward, conf.CheckHost, conf.CheckDuration) go check(forward, conf.CheckWebSite, conf.CheckDuration)
} }
} }

View File

@ -19,7 +19,7 @@ var httpMethods = [][]byte{
// mixedproxy // mixedproxy
type mixedproxy struct { type mixedproxy struct {
Proxy *proxy
http Proxy http Proxy
socks5 Proxy socks5 Proxy
ss Proxy ss Proxy
@ -28,14 +28,14 @@ type mixedproxy struct {
// MixedProxy returns a mixed proxy. // MixedProxy returns a mixed proxy.
func MixedProxy(network, addr, user, pass string, upProxy Proxy) (Proxy, error) { func MixedProxy(network, addr, user, pass string, upProxy Proxy) (Proxy, error) {
p := &mixedproxy{ p := &mixedproxy{
Proxy: upProxy, proxy: newProxy(addr, upProxy),
} }
p.http, _ = HTTPProxy(addr, upProxy) p.http, _ = HTTPProxy(addr, upProxy)
p.socks5, _ = SOCKS5Proxy(network, addr, user, pass, upProxy) p.socks5, _ = SOCKS5Proxy(network, addr, user, pass, upProxy)
if user != "" && pass != "" { if user != "" && pass != "" {
p.ss, _ = SSProxy(user, pass, upProxy) p.ss, _ = SSProxy(addr, user, pass, upProxy)
} }
return p, nil return p, nil

View File

@ -2,6 +2,7 @@ package main
import ( import (
"errors" "errors"
"io"
"net" "net"
"net/url" "net/url"
"strings" "strings"
@ -46,12 +47,12 @@ type proxy struct {
} }
// newProxy . // newProxy .
func newProxy(addr string, forward Proxy) Proxy { func newProxy(addr string, forward Proxy) *proxy {
if forward == nil { if forward == nil {
forward = Direct forward = Direct
} }
return &proxy{addr: addr, forward: forward, enabled: false} return &proxy{addr: addr, forward: forward, enabled: true}
} }
func (p *proxy) ListenAndServe() { logf("base proxy ListenAndServe") } func (p *proxy) ListenAndServe() { logf("base proxy ListenAndServe") }
@ -69,7 +70,7 @@ func (p *proxy) Dial(network, addr string) (net.Conn, error) {
// ProxyFromURL parses url and get a Proxy // ProxyFromURL parses url and get a Proxy
// TODO: table // TODO: table
func ProxyFromURL(s string, forwarders ...Proxy) (Proxy, error) { func ProxyFromURL(s string, forwarder Proxy) (Proxy, error) {
if !strings.Contains(s, "://") { if !strings.Contains(s, "://") {
s = "mixed://" + s s = "mixed://" + s
} }
@ -87,43 +88,24 @@ func ProxyFromURL(s string, forwarders ...Proxy) (Proxy, error) {
pass, _ = u.User.Password() pass, _ = u.User.Password()
} }
var proxy Proxy
if len(forwarders) == 0 {
proxy = newProxy(addr, Direct)
} else if len(forwarders) == 1 {
proxy = newProxy(addr, forwarders[0])
} else if len(forwarders) > 1 {
switch conf.Strategy {
case "rr":
proxy = newRRProxy(addr, forwarders)
logf("forward to remote servers in round robin mode.")
case "ha":
proxy = newHAProxy(addr, forwarders)
logf("forward to remote servers in high availability mode.")
default:
logf("not supported forward mode '%s', just use the first forward server.", conf.Strategy)
proxy = newProxy(addr, forwarders[0])
}
}
switch u.Scheme { switch u.Scheme {
case "ss": case "ss":
p, err := SSProxy(user, pass, proxy) p, err := SSProxy(addr, user, pass, forwarder)
return p, err return p, err
case "socks5": case "socks5":
return SOCKS5Proxy("tcp", addr, user, pass, proxy) return SOCKS5Proxy("tcp", addr, user, pass, forwarder)
case "redir": case "redir":
return RedirProxy(addr, proxy) return RedirProxy(addr, forwarder)
case "tcptun": case "tcptun":
d := strings.Split(addr, "=") d := strings.Split(addr, "=")
return TCPTunProxy(d[0], d[1], proxy) return TCPTunProxy(d[0], d[1], forwarder)
case "dnstun": case "dnstun":
d := strings.Split(addr, "=") d := strings.Split(addr, "=")
return DNSTunProxy(d[0], d[1], proxy) return DNSTunProxy(d[0], d[1], forwarder)
case "http": case "http":
return HTTPProxy(addr, proxy) return HTTPProxy(addr, forwarder)
case "mixed": case "mixed":
return MixedProxy("tcp", addr, user, pass, proxy) return MixedProxy("tcp", addr, user, pass, forwarder)
} }
return nil, errors.New("unknown schema '" + u.Scheme + "'") return nil, errors.New("unknown schema '" + u.Scheme + "'")
@ -132,6 +114,8 @@ func ProxyFromURL(s string, forwarders ...Proxy) (Proxy, error) {
// Check proxy // Check proxy
func check(p Proxy, target string, duration int) { func check(p Proxy, target string, duration int) {
firstTime := true firstTime := true
buf := make([]byte, 8)
for { for {
if !firstTime { if !firstTime {
time.Sleep(time.Duration(duration) * time.Second) time.Sleep(time.Duration(duration) * time.Second)
@ -141,15 +125,24 @@ func check(p Proxy, target string, duration int) {
startTime := time.Now() startTime := time.Now()
c, err := p.Dial("tcp", target) c, err := p.Dial("tcp", target)
if err != nil { if err != nil {
logf("proxy-check %s -> %s, set to DISABLED. error: %s", p.Addr(), conf.CheckHost, err)
p.SetEnable(false) p.SetEnable(false)
logf("proxy-check %s -> %s, set to DISABLED. error: %s", p.Addr(), target, err)
continue continue
} }
c.Close()
p.SetEnable(true)
// TODO: choose the fastest proxy. c.Write([]byte("GET / HTTP/1.0"))
dialTime := time.Since(startTime) c.Write([]byte("\r\n\r\n"))
logf("proxy-check: %s -> %s, connect time: %s", p.Addr(), conf.CheckHost, dialTime.String())
_, err = c.Read(buf)
if err != nil && err != io.EOF {
p.SetEnable(false)
logf("proxy-check %s -> %s, set to DISABLED. error: %s", p.Addr(), target, err)
} else {
p.SetEnable(true)
dialTime := time.Since(startTime)
logf("proxy-check: %s -> %s, connect time: %s", p.Addr(), target, dialTime.String())
}
c.Close()
} }
} }

View File

@ -56,7 +56,7 @@ var socks5Errors = []string{
} }
type socks5 struct { type socks5 struct {
Proxy *proxy
network, addr string network, addr string
user, password string user, password string
} }
@ -65,7 +65,7 @@ type socks5 struct {
// with an optional username and password. See RFC 1928. // with an optional username and password. See RFC 1928.
func SOCKS5Proxy(network, addr, user, pass string, upProxy Proxy) (Proxy, error) { func SOCKS5Proxy(network, addr, user, pass string, upProxy Proxy) (Proxy, error) {
s := &socks5{ s := &socks5{
Proxy: upProxy, proxy: newProxy(addr, upProxy),
addr: addr, addr: addr,
user: user, user: user,
password: pass, password: pass,

7
ss.go
View File

@ -11,19 +11,19 @@ import (
// ss // ss
type ss struct { type ss struct {
Proxy *proxy
core.StreamConnCipher core.StreamConnCipher
} }
// SSProxy returns a shadowsocks proxy. // SSProxy returns a shadowsocks proxy.
func SSProxy(method, pass string, upProxy Proxy) (Proxy, error) { func SSProxy(addr, method, pass string, upProxy Proxy) (Proxy, error) {
ciph, err := core.PickCipher(method, nil, pass) ciph, err := core.PickCipher(method, nil, pass)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
s := &ss{ s := &ss{
Proxy: upProxy, proxy: newProxy(addr, upProxy),
StreamConnCipher: ciph, StreamConnCipher: ciph,
} }
@ -86,6 +86,7 @@ func (s *ss) Serve(c net.Conn) {
// Dial connects to the address addr on the network net via the proxy. // Dial connects to the address addr on the network net via the proxy.
func (s *ss) Dial(network, addr string) (net.Conn, error) { func (s *ss) Dial(network, addr string) (net.Conn, error) {
target := ParseAddr(addr) target := ParseAddr(addr)
if target == nil { if target == nil {
return nil, errors.New("Unable to parse address: " + addr) return nil, errors.New("Unable to parse address: " + addr)

View File

@ -5,32 +5,53 @@ import (
"time" "time"
) )
// strategyProxy // newStrategyForwarder .
type strategyProxy struct { func newStrategyForwarder(strategy string, forwarders []Proxy) Proxy {
addr string var proxy Proxy
if len(forwarders) == 0 {
proxy = Direct
} else if len(forwarders) == 1 {
proxy = forwarders[0]
} else if len(forwarders) > 1 {
switch strategy {
case "rr":
proxy = newRRProxy("", forwarders)
logf("forward to remote servers in round robin mode.")
case "ha":
proxy = newHAProxy("", forwarders)
logf("forward to remote servers in high availability mode.")
default:
logf("not supported forward mode '%s', just use the first forward server.", conf.Strategy)
proxy = forwarders[0]
}
}
return proxy
}
// rrProxy
type rrProxy struct {
forwarders []Proxy forwarders []Proxy
idx int idx int
} }
// newStrategyProxy . // newRRProxy .
func newStrategyProxy(addr string, forwarders []Proxy) Proxy { func newRRProxy(addr string, forwarders []Proxy) Proxy {
if len(forwarders) == 0 { if len(forwarders) == 0 {
return Direct return Direct
} else if len(forwarders) == 1 { } else if len(forwarders) == 1 {
return newProxy(addr, forwarders[0]) return newProxy(addr, forwarders[0])
} }
return &rrProxy{forwarders: forwarders}
return &strategyProxy{addr: addr, forwarders: forwarders}
} }
func (p *strategyProxy) ListenAndServe() {} func (p *rrProxy) ListenAndServe() {}
func (p *strategyProxy) Serve(c net.Conn) {} func (p *rrProxy) Serve(c net.Conn) {}
func (p *strategyProxy) CurrentProxy() Proxy { return p.forwarders[p.idx] } func (p *rrProxy) CurrentProxy() Proxy { return p.forwarders[p.idx] }
func (p *strategyProxy) GetProxy() Proxy { return p.NextProxy() } func (p *rrProxy) GetProxy() Proxy { return p.NextProxy() }
func (p *strategyProxy) NextProxy() Proxy { func (p *rrProxy) NextProxy() Proxy {
n := len(p.forwarders) n := len(p.forwarders)
if n == 1 { if n == 1 {
return p.forwarders[0] return p.forwarders[0]
@ -52,38 +73,25 @@ func (p *strategyProxy) NextProxy() Proxy {
return p.forwarders[p.idx] return p.forwarders[p.idx]
} }
func (p *strategyProxy) Enabled() bool { return true } func (p *rrProxy) Enabled() bool { return true }
func (p *strategyProxy) SetEnable(enable bool) {} func (p *rrProxy) SetEnable(enable bool) {}
func (p *rrProxy) Check(proxy Proxy, target string, duration time.Duration) {}
func (p *strategyProxy) Check(proxy Proxy, target string, duration time.Duration) {} func (p *rrProxy) Addr() string { return "" }
func (p *rrProxy) Dial(network, addr string) (net.Conn, error) {
func (p *strategyProxy) Addr() string { return p.addr }
func (p *strategyProxy) Dial(network, addr string) (net.Conn, error) {
return p.NextProxy().Dial(network, addr) return p.NextProxy().Dial(network, addr)
} }
// round robin proxy
type rrproxy struct {
Proxy
}
// newRRProxy .
func newRRProxy(addr string, forwarders []Proxy) Proxy {
return newStrategyProxy(addr, forwarders)
}
// high availability proxy // high availability proxy
type haproxy struct { type haProxy struct {
Proxy Proxy
} }
// newHAProxy . // newHAProxy .
func newHAProxy(addr string, forwarders []Proxy) Proxy { func newHAProxy(addr string, forwarders []Proxy) Proxy {
return &haproxy{Proxy: newStrategyProxy(addr, forwarders)} return &haProxy{Proxy: newRRProxy(addr, forwarders)}
} }
func (p *haproxy) GetProxy() Proxy { func (p *haProxy) GetProxy() Proxy {
proxy := p.CurrentProxy() proxy := p.CurrentProxy()
if proxy.Enabled() == false { if proxy.Enabled() == false {
return p.NextProxy() return p.NextProxy()

View File

@ -3,7 +3,7 @@ package main
import "net" import "net"
type tcptun struct { type tcptun struct {
Proxy *proxy
addr string addr string
raddr string raddr string
} }
@ -11,7 +11,7 @@ type tcptun struct {
// TCPTunProxy returns a redirect proxy. // TCPTunProxy returns a redirect proxy.
func TCPTunProxy(addr, raddr string, upProxy Proxy) (Proxy, error) { func TCPTunProxy(addr, raddr string, upProxy Proxy) (Proxy, error) {
s := &tcptun{ s := &tcptun{
Proxy: upProxy, proxy: newProxy(addr, upProxy),
addr: addr, addr: addr,
raddr: raddr, raddr: raddr,
} }