2017-07-13 21:55:41 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2017-07-29 21:31:01 +08:00
|
|
|
"io"
|
2017-07-13 21:55:41 +08:00
|
|
|
"net"
|
|
|
|
"net/url"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// A Proxy means to establish a connection and relay it.
|
|
|
|
type Proxy interface {
|
|
|
|
// ListenAndServe as proxy server, use only in server mode.
|
|
|
|
ListenAndServe()
|
|
|
|
|
|
|
|
// Serve as proxy server, use only in server mode.
|
|
|
|
Serve(c net.Conn)
|
|
|
|
|
|
|
|
// Get address
|
|
|
|
Addr() string
|
|
|
|
|
|
|
|
// Get current proxy
|
|
|
|
CurrentProxy() Proxy
|
|
|
|
|
|
|
|
// Get a proxy according to the strategy
|
|
|
|
GetProxy() Proxy
|
|
|
|
|
|
|
|
// Switch to the next proxy
|
|
|
|
NextProxy() Proxy
|
|
|
|
|
|
|
|
// Get the status of proxy
|
|
|
|
Enabled() bool
|
|
|
|
|
|
|
|
// Set the proxy status
|
|
|
|
SetEnable(enable bool)
|
|
|
|
|
|
|
|
// Dial connects to the given address via the proxy.
|
|
|
|
Dial(network, addr string) (c net.Conn, err error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// proxy
|
|
|
|
type proxy struct {
|
|
|
|
addr string
|
|
|
|
forward Proxy
|
|
|
|
enabled bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// newProxy .
|
2017-07-29 21:31:01 +08:00
|
|
|
func newProxy(addr string, forward Proxy) *proxy {
|
2017-07-13 21:55:41 +08:00
|
|
|
if forward == nil {
|
|
|
|
forward = Direct
|
|
|
|
}
|
|
|
|
|
2017-07-29 21:31:01 +08:00
|
|
|
return &proxy{addr: addr, forward: forward, enabled: true}
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *proxy) ListenAndServe() { logf("base proxy ListenAndServe") }
|
|
|
|
func (p *proxy) Serve(c net.Conn) { logf("base proxy Serve") }
|
|
|
|
func (p *proxy) CurrentProxy() Proxy { return p.forward }
|
|
|
|
func (p *proxy) GetProxy() Proxy { return p.forward }
|
|
|
|
func (p *proxy) NextProxy() Proxy { return p.forward }
|
|
|
|
func (p *proxy) Enabled() bool { return p.enabled }
|
|
|
|
func (p *proxy) SetEnable(enable bool) { p.enabled = enable }
|
|
|
|
func (p *proxy) Addr() string { return p.addr }
|
|
|
|
|
|
|
|
func (p *proxy) Dial(network, addr string) (net.Conn, error) {
|
|
|
|
return p.forward.Dial(network, addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ProxyFromURL parses url and get a Proxy
|
|
|
|
// TODO: table
|
2017-07-29 21:31:01 +08:00
|
|
|
func ProxyFromURL(s string, forwarder Proxy) (Proxy, error) {
|
2017-07-13 21:55:41 +08:00
|
|
|
if !strings.Contains(s, "://") {
|
|
|
|
s = "mixed://" + s
|
|
|
|
}
|
|
|
|
|
|
|
|
u, err := url.Parse(s)
|
|
|
|
if err != nil {
|
|
|
|
logf("parse err: %s", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
addr := u.Host
|
|
|
|
var user, pass string
|
|
|
|
if u.User != nil {
|
|
|
|
user = u.User.Username()
|
|
|
|
pass, _ = u.User.Password()
|
|
|
|
}
|
|
|
|
|
|
|
|
switch u.Scheme {
|
|
|
|
case "ss":
|
2017-07-29 21:31:01 +08:00
|
|
|
p, err := SSProxy(addr, user, pass, forwarder)
|
2017-07-13 21:55:41 +08:00
|
|
|
return p, err
|
|
|
|
case "socks5":
|
2017-07-29 21:31:01 +08:00
|
|
|
return SOCKS5Proxy("tcp", addr, user, pass, forwarder)
|
2017-07-13 21:55:41 +08:00
|
|
|
case "redir":
|
2017-07-29 21:31:01 +08:00
|
|
|
return RedirProxy(addr, forwarder)
|
2017-07-13 21:55:41 +08:00
|
|
|
case "tcptun":
|
|
|
|
d := strings.Split(addr, "=")
|
2017-07-29 21:31:01 +08:00
|
|
|
return TCPTunProxy(d[0], d[1], forwarder)
|
2017-07-13 21:55:41 +08:00
|
|
|
case "dnstun":
|
|
|
|
d := strings.Split(addr, "=")
|
2017-07-29 21:31:01 +08:00
|
|
|
return DNSTunProxy(d[0], d[1], forwarder)
|
2017-07-13 21:55:41 +08:00
|
|
|
case "http":
|
2017-07-29 21:31:01 +08:00
|
|
|
return HTTPProxy(addr, forwarder)
|
2017-07-13 21:55:41 +08:00
|
|
|
case "mixed":
|
2017-07-29 21:31:01 +08:00
|
|
|
return MixedProxy("tcp", addr, user, pass, forwarder)
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil, errors.New("unknown schema '" + u.Scheme + "'")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check proxy
|
|
|
|
func check(p Proxy, target string, duration int) {
|
|
|
|
firstTime := true
|
2017-07-29 21:31:01 +08:00
|
|
|
buf := make([]byte, 8)
|
|
|
|
|
2017-07-13 21:55:41 +08:00
|
|
|
for {
|
|
|
|
if !firstTime {
|
|
|
|
time.Sleep(time.Duration(duration) * time.Second)
|
|
|
|
}
|
|
|
|
firstTime = false
|
|
|
|
|
2017-07-15 10:41:52 +08:00
|
|
|
startTime := time.Now()
|
2017-07-13 21:55:41 +08:00
|
|
|
c, err := p.Dial("tcp", target)
|
|
|
|
if err != nil {
|
|
|
|
p.SetEnable(false)
|
2017-07-29 21:31:01 +08:00
|
|
|
logf("proxy-check %s -> %s, set to DISABLED. error: %s", p.Addr(), target, err)
|
2017-07-13 21:55:41 +08:00
|
|
|
continue
|
|
|
|
}
|
2017-07-16 12:16:50 +08:00
|
|
|
|
2017-07-29 21:31:01 +08:00
|
|
|
c.Write([]byte("GET / HTTP/1.0"))
|
|
|
|
c.Write([]byte("\r\n\r\n"))
|
|
|
|
|
|
|
|
_, 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()
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
}
|