diff --git a/conf.go b/conf.go index b6217a3..7042228 100644 --- a/conf.go +++ b/conf.go @@ -14,14 +14,15 @@ import ( var flag = conflag.New() var conf struct { - Verbose bool - Strategy string - CheckWebSite string - CheckInterval int - Listen []string - Forward []string - RuleFile []string - RulesDir string + Verbose bool + + Listen []string + + Forward []string + StrategyConfig + + RuleFile []string + RulesDir string DNS string DNSServer []string @@ -37,11 +38,13 @@ var conf struct { func confInit() { flag.BoolVar(&conf.Verbose, "verbose", false, "verbose mode") - flag.StringVar(&conf.Strategy, "strategy", "rr", "forward strategy, default: rr") - flag.StringVar(&conf.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80") - flag.IntVar(&conf.CheckInterval, "checkduration", 30, "proxy check interval(seconds)") flag.StringSliceUniqVar(&conf.Listen, "listen", nil, "listen url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS") + flag.StringSliceUniqVar(&conf.Forward, "forward", nil, "forward url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS[,SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS]") + flag.StringVar(&conf.StrategyConfig.Strategy, "strategy", "rr", "forward strategy, default: rr") + flag.StringVar(&conf.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80") + flag.IntVar(&conf.StrategyConfig.CheckInterval, "checkduration", 30, "proxy check interval(seconds)") + flag.StringSliceUniqVar(&conf.RuleFile, "rulefile", nil, "rule file path") flag.StringVar(&conf.RulesDir, "rules-dir", "", "rule file folder") @@ -119,10 +122,8 @@ func listDir(dirPth string, suffix string) (files []string, err error) { type RuleConf struct { name string - Forward []string - Strategy string - CheckWebSite string - CheckInterval int + Forward []string + StrategyConfig DNSServer []string IPSet string @@ -138,9 +139,9 @@ func NewRuleConfFromFile(ruleFile string) (*RuleConf, error) { f := conflag.NewFromFile("rule", ruleFile) f.StringSliceUniqVar(&p.Forward, "forward", nil, "forward url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS[,SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS]") - f.StringVar(&p.Strategy, "strategy", "rr", "forward strategy, default: rr") - f.StringVar(&p.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80") - f.IntVar(&p.CheckInterval, "checkduration", 30, "proxy check interval(seconds)") + f.StringVar(&p.StrategyConfig.Strategy, "strategy", "rr", "forward strategy, default: rr") + f.StringVar(&p.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80") + f.IntVar(&p.StrategyConfig.CheckInterval, "checkduration", 30, "proxy check interval(seconds)") f.StringSliceUniqVar(&p.DNSServer, "dnsserver", nil, "remote dns server") f.StringVar(&p.IPSet, "ipset", "", "ipset name") diff --git a/main.go b/main.go index 0ab0216..48a3a05 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,6 @@ import ( stdlog "log" "os" "os/signal" - "strings" "syscall" "github.com/nadoo/glider/common/log" @@ -25,39 +24,28 @@ import ( ) // VERSION . -const VERSION = "0.6.6" - -func dialerFromConf() proxy.Dialer { - // global forwarders in xx.conf - var fwdrs []proxy.Dialer - for _, chain := range conf.Forward { - var fwdr proxy.Dialer - var err error - for _, url := range strings.Split(chain, ",") { - fwdr, err = proxy.DialerFromURL(url, fwdr) - if err != nil { - log.Fatal(err) - } - } - fwdrs = append(fwdrs, fwdr) - } - - return NewStrategyDialer(conf.Strategy, fwdrs, conf.CheckWebSite, conf.CheckInterval) -} +const VERSION = "0.6.7" func main() { + // Config confInit() + + // Log log.F = func(f string, v ...interface{}) { if conf.Verbose { stdlog.Printf(f, v...) } } - dialer := NewRuleDialer(conf.rules, dialerFromConf()) - ipsetM, _ := NewIPSetManager(conf.IPSet, conf.rules) - if conf.DNS != "" { + // Forwarder + dialer := NewRuleDialer(conf.rules, StrategyDialer(conf.Forward, &conf.StrategyConfig)) + // IPSet manager + ipsetM, _ := NewIPSetManager(conf.IPSet, conf.rules) + + // DNS Server + if conf.DNS != "" { dnscfg := &dns.Config{ Timeout: conf.DNSTimeout, MaxTTL: conf.DNSMaxTTL, @@ -91,8 +79,9 @@ func main() { go d.ListenAndServe() } + // Servers for _, listen := range conf.Listen { - local, err := proxy.ServerFromURL(listen, dialer) + local, err := proxy.ServerFromURL(listen, proxy.NewForwarder(dialer)) if err != nil { log.Fatal(err) } diff --git a/proxy/forwarder.go b/proxy/forwarder.go new file mode 100644 index 0000000..f25f5e6 --- /dev/null +++ b/proxy/forwarder.go @@ -0,0 +1,49 @@ +package proxy + +import ( + "strings" +) + +// Forwarder is a forwarder +type Forwarder struct { + Dialer + addr string + disabled bool + failures int + priority int + weight int +} + +// ForwarderFromURL returns a new forwarder +func ForwarderFromURL(s string) (*Forwarder, error) { + var d Dialer + var err error + for _, url := range strings.Split(s, ",") { + d, err = DialerFromURL(url, d) + if err != nil { + return nil, err + } + } + + return &Forwarder{Dialer: d}, nil +} + +// NewForwarder . +func NewForwarder(dialer Dialer) *Forwarder { + return &Forwarder{Dialer: dialer, addr: dialer.Addr()} +} + +// Addr . +func (f *Forwarder) Addr() string { + return f.addr +} + +// Enable . +func (f *Forwarder) Enable(b bool) { + f.disabled = !b +} + +// Enabled . +func (f *Forwarder) Enabled() bool { + return !f.disabled +} diff --git a/proxy/http/dialer.go b/proxy/http/dialer.go new file mode 100644 index 0000000..e522b93 --- /dev/null +++ b/proxy/http/dialer.go @@ -0,0 +1,90 @@ +package http + +import ( + "bufio" + "bytes" + "encoding/base64" + "errors" + "net" + "net/textproto" + + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterDialer("http", CreateDialer) +} + +// Dialer struct +type Dialer struct { + *HTTP + dialer proxy.Dialer +} + +// NewDialer returns a proxy dialer +func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) { + h, err := NewHTTP(s) + if err != nil { + return nil, err + } + + d := &Dialer{HTTP: h, dialer: dialer} + return d, nil +} + +// CreateDialer returns a proxy dialer +func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewDialer(s, dialer) +} + +// Addr returns dialer's address +func (s *Dialer) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} + +// NextDialer returns the next dialer +func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } + +// Dial establishes a connection to the addr +func (s *Dialer) Dial(network, addr string) (net.Conn, error) { + rc, err := s.dialer.Dial(network, s.addr) + if err != nil { + log.F("[http] dial to %s error: %s", s.addr, err) + return nil, err + } + + var buf bytes.Buffer + buf.Write([]byte("CONNECT " + addr + " HTTP/1.1\r\n")) + buf.Write([]byte("Proxy-Connection: Keep-Alive\r\n")) + + if s.user != "" && s.password != "" { + auth := s.user + ":" + s.password + buf.Write([]byte("Proxy-Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) + "\r\n")) + } + + //header ended + buf.Write([]byte("\r\n")) + rc.Write(buf.Bytes()) + + respR := bufio.NewReader(rc) + respTP := textproto.NewReader(respR) + _, code, _, ok := parseFirstLine(respTP) + if ok && code == "200" { + return rc, err + } else if code == "407" { + log.F("[http] authencation needed by proxy %s", s.addr) + } else if code == "405" { + log.F("[http] 'CONNECT' method not allowed by proxy %s", s.addr) + } + + return nil, errors.New("[http] can not connect remote address: " + addr + ". error code: " + code) +} + +// DialUDP returns a PacketConn to the addr +func (s *Dialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { + return nil, nil, errors.New("http client does not support udp") +} diff --git a/proxy/http/http.go b/proxy/http/http.go index 49615e3..4aea435 100644 --- a/proxy/http/http.go +++ b/proxy/http/http.go @@ -4,38 +4,23 @@ package http import ( - "bufio" "bytes" - "encoding/base64" - "errors" - "fmt" - "io" - "net" "net/textproto" "net/url" "strings" - "time" - "github.com/nadoo/glider/common/conn" "github.com/nadoo/glider/common/log" - "github.com/nadoo/glider/proxy" ) // HTTP struct type HTTP struct { - dialer proxy.Dialer addr string user string password string } -func init() { - proxy.RegisterDialer("http", NewHTTPDialer) - proxy.RegisterServer("http", NewHTTPServer) -} - -// NewHTTP returns a http proxy. -func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) { +// NewHTTP returns a http base struct +func NewHTTP(s string) (*HTTP, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -47,7 +32,6 @@ func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) { pass, _ := u.User.Password() h := &HTTP{ - dialer: dialer, addr: addr, user: user, password: pass, @@ -56,215 +40,12 @@ func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) { return h, nil } -// NewHTTPDialer returns a http proxy dialer. -func NewHTTPDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { - return NewHTTP(s, dialer) -} - -// NewHTTPServer returns a http proxy server. -func NewHTTPServer(s string, dialer proxy.Dialer) (proxy.Server, error) { - return NewHTTP(s, dialer) -} - -// ListenAndServe . -func (s *HTTP) ListenAndServe() { - l, err := net.Listen("tcp", s.addr) - if err != nil { - log.F("failed to listen on %s: %v", s.addr, err) - return - } - defer l.Close() - - log.F("listening TCP on %s", s.addr) - - for { - c, err := l.Accept() - if err != nil { - log.F("[http] failed to accept: %v", err) - continue - } - - go s.Serve(c) - } -} - -// Serve . -func (s *HTTP) Serve(c net.Conn) { - defer c.Close() - - if c, ok := c.(*net.TCPConn); ok { - c.SetKeepAlive(true) - } - - reqR := bufio.NewReader(c) - reqTP := textproto.NewReader(reqR) - method, requestURI, proto, ok := parseFirstLine(reqTP) - if !ok { - return - } - - if method == "CONNECT" { - s.servHTTPS(method, requestURI, proto, c) - return - } - - reqHeader, err := reqTP.ReadMIMEHeader() - if err != nil { - log.F("read header error:%s", err) - return - } - cleanHeaders(reqHeader) - - // tell the remote server not to keep alive - reqHeader.Set("Connection", "close") - - u, err := url.ParseRequestURI(requestURI) - if err != nil { - log.F("[http] parse request url error: %s", err) - return - } - - var tgt = u.Host - if !strings.Contains(u.Host, ":") { - tgt += ":80" - } - - rc, err := s.dialer.Dial("tcp", tgt) - if err != nil { - fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto) - log.F("[http] failed to dial: %v", err) - return - } - defer rc.Close() - - // GET http://example.com/a/index.htm HTTP/1.1 --> - // GET /a/index.htm HTTP/1.1 - u.Scheme = "" - u.Host = "" - uri := u.String() - - var reqBuf bytes.Buffer - writeFirstLine(method, uri, proto, &reqBuf) - writeHeaders(reqHeader, &reqBuf) - - // send request to remote server - rc.Write(reqBuf.Bytes()) - - // copy the left request bytes to remote server. eg. length specificed or chunked body. - go func() { - if _, err := reqR.Peek(1); err == nil { - io.Copy(rc, reqR) - rc.SetDeadline(time.Now()) - c.SetDeadline(time.Now()) - } - }() - - respR := bufio.NewReader(rc) - respTP := textproto.NewReader(respR) - proto, code, status, ok := parseFirstLine(respTP) - if !ok { - return - } - - respHeader, err := respTP.ReadMIMEHeader() - if err != nil { - log.F("[http] read header error:%s", err) - return - } - - respHeader.Set("Proxy-Connection", "close") - respHeader.Set("Connection", "close") - - var respBuf bytes.Buffer - writeFirstLine(proto, code, status, &respBuf) - writeHeaders(respHeader, &respBuf) - - log.F("[http] %s <-> %s", c.RemoteAddr(), tgt) - c.Write(respBuf.Bytes()) - - io.Copy(c, respR) - -} - -func (s *HTTP) servHTTPS(method, requestURI, proto string, c net.Conn) { - rc, err := s.dialer.Dial("tcp", requestURI) - if err != nil { - c.Write([]byte(proto)) - c.Write([]byte(" 502 ERROR\r\n\r\n")) - log.F("[http] failed to dial: %v", err) - return - } - - c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) - - log.F("[http] %s <-> %s [c]", c.RemoteAddr(), requestURI) - - _, _, err = conn.Relay(c, rc) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return // ignore i/o timeout - } - log.F("relay error: %v", err) - } -} - -// Addr returns forwarder's address -func (s *HTTP) Addr() string { - if s.addr == "" { - return s.dialer.Addr() - } - return s.addr -} - -// NextDialer returns the next dialer -func (s *HTTP) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } - -// Dial connects to the address addr on the network net via the proxy. -func (s *HTTP) Dial(network, addr string) (net.Conn, error) { - rc, err := s.dialer.Dial(network, s.addr) - if err != nil { - log.F("[http] dial to %s error: %s", s.addr, err) - return nil, err - } - - var buf bytes.Buffer - buf.Write([]byte("CONNECT " + addr + " HTTP/1.1\r\n")) - buf.Write([]byte("Proxy-Connection: Keep-Alive\r\n")) - - if s.user != "" && s.password != "" { - auth := s.user + ":" + s.password - buf.Write([]byte("Proxy-Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) + "\r\n")) - } - - //header ended - buf.Write([]byte("\r\n")) - rc.Write(buf.Bytes()) - - respR := bufio.NewReader(rc) - respTP := textproto.NewReader(respR) - _, code, _, ok := parseFirstLine(respTP) - if ok && code == "200" { - return rc, err - } else if code == "407" { - log.F("[http] authencation needed by proxy %s", s.addr) - } else if code == "405" { - log.F("[http] 'CONNECT' method not allowed by proxy %s", s.addr) - } - - return nil, errors.New("[http] can not connect remote address: " + addr + ". error code: " + code) -} - -// DialUDP connects to the given address via the proxy. -func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { - return nil, nil, errors.New("http client does not support udp") -} - // parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts. func parseFirstLine(tp *textproto.Reader) (r1, r2, r3 string, ok bool) { line, err := tp.ReadLine() // log.F("first line: %s", line) if err != nil { - log.F("[http] read first line error:%s", err) + log.F("[http] read first line error: %s", err) return } diff --git a/proxy/http/server.go b/proxy/http/server.go new file mode 100644 index 0000000..e59b4fe --- /dev/null +++ b/proxy/http/server.go @@ -0,0 +1,184 @@ +package http + +import ( + "bufio" + "bytes" + "fmt" + "io" + "net" + "net/textproto" + "net/url" + "strings" + "time" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterServer("http", CreateServer) +} + +// Server struct +type Server struct { + *HTTP + *proxy.Forwarder +} + +// NewServer returns a local proxy server +func NewServer(s string, f *proxy.Forwarder) (*Server, error) { + h, err := NewHTTP(s) + if err != nil { + return nil, err + } + server := &Server{HTTP: h, Forwarder: f} + return server, nil +} + +// CreateServer returns a local proxy server +func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) { + return NewServer(s, f) +} + +// ListenAndServe serves requests from clients +func (s *Server) ListenAndServe() { + l, err := net.Listen("tcp", s.addr) + if err != nil { + log.F("failed to listen on %s: %v", s.addr, err) + return + } + defer l.Close() + + log.F("listening TCP on %s", s.addr) + + for { + c, err := l.Accept() + if err != nil { + log.F("[http] failed to accept: %v", err) + continue + } + + go s.Serve(c) + } +} + +// Serve . +func (s *Server) Serve(c net.Conn) { + defer c.Close() + + if c, ok := c.(*net.TCPConn); ok { + c.SetKeepAlive(true) + } + + reqR := bufio.NewReader(c) + reqTP := textproto.NewReader(reqR) + method, requestURI, proto, ok := parseFirstLine(reqTP) + if !ok { + return + } + + if method == "CONNECT" { + s.servHTTPS(method, requestURI, proto, c) + return + } + + reqHeader, err := reqTP.ReadMIMEHeader() + if err != nil { + log.F("read header error:%s", err) + return + } + cleanHeaders(reqHeader) + + // tell the remote server not to keep alive + reqHeader.Set("Connection", "close") + + u, err := url.ParseRequestURI(requestURI) + if err != nil { + log.F("[http] parse request url error: %s", err) + return + } + + var tgt = u.Host + if !strings.Contains(u.Host, ":") { + tgt += ":80" + } + + rc, err := s.Dial("tcp", tgt) + if err != nil { + fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto) + log.F("[http] failed to dial: %v", err) + return + } + defer rc.Close() + + // GET http://example.com/a/index.htm HTTP/1.1 --> + // GET /a/index.htm HTTP/1.1 + u.Scheme = "" + u.Host = "" + uri := u.String() + + var reqBuf bytes.Buffer + writeFirstLine(method, uri, proto, &reqBuf) + writeHeaders(reqHeader, &reqBuf) + + // send request to remote server + rc.Write(reqBuf.Bytes()) + + // copy the left request bytes to remote server. eg. length specificed or chunked body. + go func() { + if _, err := reqR.Peek(1); err == nil { + io.Copy(rc, reqR) + rc.SetDeadline(time.Now()) + c.SetDeadline(time.Now()) + } + }() + + respR := bufio.NewReader(rc) + respTP := textproto.NewReader(respR) + proto, code, status, ok := parseFirstLine(respTP) + if !ok { + return + } + + respHeader, err := respTP.ReadMIMEHeader() + if err != nil { + log.F("[http] read header error:%s", err) + return + } + + respHeader.Set("Proxy-Connection", "close") + respHeader.Set("Connection", "close") + + var respBuf bytes.Buffer + writeFirstLine(proto, code, status, &respBuf) + writeHeaders(respHeader, &respBuf) + + log.F("[http] %s <-> %s", c.RemoteAddr(), tgt) + c.Write(respBuf.Bytes()) + + io.Copy(c, respR) + +} + +func (s *Server) servHTTPS(method, requestURI, proto string, c net.Conn) { + rc, err := s.Dial("tcp", requestURI) + if err != nil { + c.Write([]byte(proto)) + c.Write([]byte(" 502 ERROR\r\n\r\n")) + log.F("[http] failed to dial: %v", err) + return + } + + c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) + + log.F("[http] %s <-> %s [c]", c.RemoteAddr(), requestURI) + + _, _, err = conn.Relay(c, rc) + if err != nil { + if err, ok := err.(net.Error); ok && err.Timeout() { + return // ignore i/o timeout + } + log.F("relay error: %v", err) + } +} diff --git a/proxy/mixed/mixed.go b/proxy/mixed/server.go similarity index 77% rename from proxy/mixed/mixed.go rename to proxy/mixed/server.go index 5780d8b..9aa58f1 100644 --- a/proxy/mixed/mixed.go +++ b/proxy/mixed/server.go @@ -12,6 +12,10 @@ import ( "github.com/nadoo/glider/proxy/socks5" ) +func init() { + proxy.RegisterServer("mixed", NewMixedProxyServer) +} + // https://www.ietf.org/rfc/rfc2616.txt, http methods must be uppercase. var httpMethods = [...][]byte{ []byte("GET"), @@ -24,45 +28,40 @@ var httpMethods = [...][]byte{ []byte("TRACE"), } -// MixedProxy struct -type MixedProxy struct { - dialer proxy.Dialer +// Server struct +type Server struct { + *proxy.Forwarder addr string - - http *http.HTTP - socks5 *socks5.SOCKS5 -} - -func init() { - proxy.RegisterServer("mixed", NewMixedProxyServer) + http *http.Server + socks5 *socks5.Server } // NewMixedProxy returns a mixed proxy. -func NewMixedProxy(s string, dialer proxy.Dialer) (*MixedProxy, error) { +func NewMixedProxy(s string, f *proxy.Forwarder) (*Server, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) return nil, err } - p := &MixedProxy{ - dialer: dialer, - addr: u.Host, + p := &Server{ + Forwarder: f, + addr: u.Host, } - p.http, _ = http.NewHTTP(s, dialer) - p.socks5, _ = socks5.NewSOCKS5(s, dialer) + p.http, _ = http.NewServer(s, f) + p.socks5, _ = socks5.NewServer(s, f) return p, nil } // NewMixedProxyServer returns a mixed proxy server. -func NewMixedProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) { - return NewMixedProxy(s, dialer) +func NewMixedProxyServer(s string, f *proxy.Forwarder) (proxy.Server, error) { + return NewMixedProxy(s, f) } // ListenAndServe . -func (p *MixedProxy) ListenAndServe() { +func (p *Server) ListenAndServe() { go p.socks5.ListenAndServeUDP() @@ -86,7 +85,7 @@ func (p *MixedProxy) ListenAndServe() { } // Serve . -func (p *MixedProxy) Serve(c net.Conn) { +func (p *Server) Serve(c net.Conn) { defer c.Close() if c, ok := c.(*net.TCPConn); ok { diff --git a/proxy/redir/redir_linux.go b/proxy/redir/server_linux.go similarity index 86% rename from proxy/redir/redir_linux.go rename to proxy/redir/server_linux.go index 1d202fc..590a33f 100644 --- a/proxy/redir/redir_linux.go +++ b/proxy/redir/server_linux.go @@ -16,6 +16,10 @@ import ( "github.com/nadoo/glider/proxy" ) +func init() { + proxy.RegisterServer("redir", CreateServer) +} + const ( // SO_ORIGINAL_DST from linux/include/uapi/linux/netfilter_ipv4.h SO_ORIGINAL_DST = 80 @@ -23,40 +27,31 @@ const ( IP6T_SO_ORIGINAL_DST = 80 ) -// RedirProxy struct -type RedirProxy struct { - dialer proxy.Dialer - addr string +// Server struct +type Server struct { + addr string + *proxy.Forwarder } -func init() { - proxy.RegisterServer("redir", NewRedirServer) -} - -// NewRedirProxy returns a redirect proxy. -func NewRedirProxy(s string, dialer proxy.Dialer) (*RedirProxy, error) { +// NewServer returns a local proxy server +func NewServer(s string, f *proxy.Forwarder) (*Server, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) return nil, err } - addr := u.Host - r := &RedirProxy{ - dialer: dialer, - addr: addr, - } - - return r, nil + server := &Server{addr: u.Host, Forwarder: f} + return server, nil } -// NewRedirServer returns a redir server. -func NewRedirServer(s string, dialer proxy.Dialer) (proxy.Server, error) { - return NewRedirProxy(s, dialer) +// CreateServer returns a local proxy server +func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) { + return NewServer(s, f) } // ListenAndServe . -func (s *RedirProxy) ListenAndServe() { +func (s *Server) ListenAndServe() { l, err := net.Listen("tcp", s.addr) if err != nil { log.F("[redir] failed to listen on %s: %v", s.addr, err) @@ -85,7 +80,7 @@ func (s *RedirProxy) ListenAndServe() { return } - rc, err := s.dialer.Dial("tcp", tgt.String()) + rc, err := s.Dial("tcp", tgt.String()) if err != nil { log.F("[redir] failed to connect to target: %v", err) return diff --git a/proxy/redir/redir_linux_386.go b/proxy/redir/server_linux_386.go similarity index 100% rename from proxy/redir/redir_linux_386.go rename to proxy/redir/server_linux_386.go diff --git a/proxy/redir/redir_linux_other.go b/proxy/redir/server_linux_other.go similarity index 100% rename from proxy/redir/redir_linux_other.go rename to proxy/redir/server_linux_other.go diff --git a/proxy/server.go b/proxy/server.go index 4bc4e1d..3217722 100644 --- a/proxy/server.go +++ b/proxy/server.go @@ -15,7 +15,7 @@ type Server interface { } // ServerCreator is a function to create proxy servers. -type ServerCreator func(s string, dialer Dialer) (Server, error) +type ServerCreator func(s string, f *Forwarder) (Server, error) var ( serverMap = make(map[string]ServerCreator) @@ -27,7 +27,7 @@ func RegisterServer(name string, c ServerCreator) { } // ServerFromURL calls the registered creator to create proxy servers. -func ServerFromURL(s string, dialer Dialer) (Server, error) { +func ServerFromURL(s string, f *Forwarder) (Server, error) { if !strings.Contains(s, "://") { s = "mixed://" + s } @@ -38,13 +38,13 @@ func ServerFromURL(s string, dialer Dialer) (Server, error) { return nil, err } - if dialer == nil { - dialer = Direct + if f == nil { + f = NewForwarder(Direct) } c, ok := serverMap[strings.ToLower(u.Scheme)] if ok { - return c(s, dialer) + return c(s, f) } return nil, errors.New("unknown scheme '" + u.Scheme + "'") diff --git a/proxy/socks5/dialer.go b/proxy/socks5/dialer.go new file mode 100644 index 0000000..383cc6a --- /dev/null +++ b/proxy/socks5/dialer.go @@ -0,0 +1,251 @@ +package socks5 + +import ( + "errors" + "io" + "net" + "strconv" + + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/common/socks" + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterDialer("socks5", CreateDialer) +} + +// Dialer struct +type Dialer struct { + *SOCKS5 + dialer proxy.Dialer +} + +// NewDialer returns a proxy dialer +func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) { + socks, err := NewSOCKS5(s) + if err != nil { + return nil, err + } + + d := &Dialer{SOCKS5: socks, dialer: dialer} + return d, nil +} + +// CreateDialer returns a proxy dialer +func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewDialer(s, dialer) +} + +// Addr returns dialer's address +func (s *Dialer) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} + +// NextDialer returns the next dialer +func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } + +// Dial establishes a connection to the addr +func (s *Dialer) Dial(network, addr string) (net.Conn, error) { + switch network { + case "tcp", "tcp6", "tcp4": + default: + return nil, errors.New("[socks5]: no support for connection type " + network) + } + + c, err := s.dialer.Dial(network, s.addr) + if err != nil { + log.F("dial to %s error: %s", s.addr, err) + return nil, err + } + + if err := s.connect(c, addr); err != nil { + c.Close() + return nil, err + } + + return c, nil +} + +// DialUDP returns a PacketConn to the addr +func (s *Dialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { + c, err := s.dialer.Dial("tcp", s.addr) + if err != nil { + log.F("[socks5] dialudp dial tcp to %s error: %s", s.addr, err) + return nil, nil, err + } + + // send VER, NMETHODS, METHODS + c.Write([]byte{5, 1, 0}) + + buf := make([]byte, socks.MaxAddrLen) + // read VER METHOD + if _, err := io.ReadFull(c, buf[:2]); err != nil { + return nil, nil, err + } + + dstAddr := socks.ParseAddr(addr) + // write VER CMD RSV ATYP DST.ADDR DST.PORT + c.Write(append([]byte{5, socks.CmdUDPAssociate, 0}, dstAddr...)) + + // read VER REP RSV ATYP BND.ADDR BND.PORT + if _, err := io.ReadFull(c, buf[:3]); err != nil { + return nil, nil, err + } + + rep := buf[1] + if rep != 0 { + log.F("[socks5] server reply: %d, not succeeded", rep) + return nil, nil, errors.New("server connect failed") + } + + uAddr, err := socks.ReadAddrBuf(c, buf) + if err != nil { + return nil, nil, err + } + + pc, nextHop, err := s.dialer.DialUDP(network, uAddr.String()) + if err != nil { + log.F("[socks5] dialudp to %s error: %s", uAddr.String(), err) + return nil, nil, err + } + + pkc := NewPktConn(pc, nextHop, dstAddr, true, c) + return pkc, nextHop, err +} + +// connect takes an existing connection to a socks5 proxy server, +// and commands the server to extend that connection to target, +// which must be a canonical address with a host and port. +func (s *Dialer) connect(conn net.Conn, target string) error { + host, portStr, err := net.SplitHostPort(target) + if err != nil { + return err + } + + port, err := strconv.Atoi(portStr) + if err != nil { + return errors.New("proxy: failed to parse port number: " + portStr) + } + if port < 1 || port > 0xffff { + return errors.New("proxy: port number out of range: " + portStr) + } + + // the size here is just an estimate + buf := make([]byte, 0, 6+len(host)) + + buf = append(buf, Version) + if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { + buf = append(buf, 2 /* num auth methods */, socks.AuthNone, socks.AuthPassword) + } else { + buf = append(buf, 1 /* num auth methods */, socks.AuthNone) + } + + if _, err := conn.Write(buf); err != nil { + return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + if buf[0] != 5 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) + } + if buf[1] == 0xff { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") + } + + if buf[1] == socks.AuthPassword { + buf = buf[:0] + buf = append(buf, 1 /* password protocol version */) + buf = append(buf, uint8(len(s.user))) + buf = append(buf, s.user...) + buf = append(buf, uint8(len(s.password))) + buf = append(buf, s.password...) + + if _, err := conn.Write(buf); err != nil { + return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if buf[1] != 0 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") + } + } + + buf = buf[:0] + buf = append(buf, Version, socks.CmdConnect, 0 /* reserved */) + + if ip := net.ParseIP(host); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + buf = append(buf, socks.ATypIP4) + ip = ip4 + } else { + buf = append(buf, socks.ATypIP6) + } + buf = append(buf, ip...) + } else { + if len(host) > 255 { + return errors.New("proxy: destination hostname too long: " + host) + } + buf = append(buf, socks.ATypDomain) + buf = append(buf, byte(len(host))) + buf = append(buf, host...) + } + buf = append(buf, byte(port>>8), byte(port)) + + if _, err := conn.Write(buf); err != nil { + return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:4]); err != nil { + return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + failure := "unknown error" + if int(buf[1]) < len(socks.Errors) { + failure = socks.Errors[buf[1]].Error() + } + + if len(failure) > 0 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) + } + + bytesToDiscard := 0 + switch buf[3] { + case socks.ATypIP4: + bytesToDiscard = net.IPv4len + case socks.ATypIP6: + bytesToDiscard = net.IPv6len + case socks.ATypDomain: + _, err := io.ReadFull(conn, buf[:1]) + if err != nil { + return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + bytesToDiscard = int(buf[0]) + default: + return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) + } + + if cap(buf) < bytesToDiscard { + buf = make([]byte, bytesToDiscard) + } else { + buf = buf[:bytesToDiscard] + } + if _, err := io.ReadFull(conn, buf); err != nil { + return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + // Also need to discard the port number + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + return nil +} diff --git a/proxy/socks5/server.go b/proxy/socks5/server.go new file mode 100644 index 0000000..a1eb984 --- /dev/null +++ b/proxy/socks5/server.go @@ -0,0 +1,214 @@ +package socks5 + +import ( + "io" + "net" + "sync" + "time" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/common/socks" + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterServer("socks5", CreateServer) +} + +// Server struct +type Server struct { + *SOCKS5 + *proxy.Forwarder +} + +// NewServer returns a local proxy server +func NewServer(s string, f *proxy.Forwarder) (*Server, error) { + h, err := NewSOCKS5(s) + if err != nil { + return nil, err + } + server := &Server{SOCKS5: h, Forwarder: f} + return server, nil +} + +// CreateServer returns a local proxy server +func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) { + return NewServer(s, f) +} + +// ListenAndServe serves socks5 requests. +func (s *Server) ListenAndServe() { + go s.ListenAndServeUDP() + s.ListenAndServeTCP() +} + +// ListenAndServeTCP . +func (s *Server) ListenAndServeTCP() { + l, err := net.Listen("tcp", s.addr) + if err != nil { + log.F("[socks5] failed to listen on %s: %v", s.addr, err) + return + } + + log.F("[socks5] listening TCP on %s", s.addr) + + for { + c, err := l.Accept() + if err != nil { + log.F("[socks5] failed to accept: %v", err) + continue + } + + go s.ServeTCP(c) + } +} + +// ServeTCP . +func (s *Server) ServeTCP(c net.Conn) { + defer c.Close() + + if c, ok := c.(*net.TCPConn); ok { + c.SetKeepAlive(true) + } + + tgt, err := s.handshake(c) + if err != nil { + // UDP: keep the connection until disconnect then free the UDP socket + if err == socks.Errors[9] { + buf := []byte{} + // block here + for { + _, err := c.Read(buf) + if err, ok := err.(net.Error); ok && err.Timeout() { + continue + } + // log.F("[socks5] servetcp udp associate end") + return + } + } + + log.F("[socks5] failed to get target address: %v", err) + return + } + + rc, err := s.Dial("tcp", tgt.String()) + if err != nil { + log.F("[socks5] failed to connect to target: %v", err) + return + } + defer rc.Close() + + log.F("[socks5] %s <-> %s", c.RemoteAddr(), tgt) + + _, _, err = conn.Relay(c, rc) + if err != nil { + if err, ok := err.(net.Error); ok && err.Timeout() { + return // ignore i/o timeout + } + log.F("[socks5] relay error: %v", err) + } +} + +// ListenAndServeUDP serves udp requests. +func (s *Server) ListenAndServeUDP() { + lc, err := net.ListenPacket("udp", s.addr) + if err != nil { + log.F("[socks5-udp] failed to listen on %s: %v", s.addr, err) + return + } + defer lc.Close() + + log.F("[socks5-udp] listening UDP on %s", s.addr) + + var nm sync.Map + buf := make([]byte, conn.UDPBufSize) + + for { + c := NewPktConn(lc, nil, nil, true, nil) + + n, raddr, err := c.ReadFrom(buf) + if err != nil { + log.F("[socks5-udp] remote read error: %v", err) + continue + } + + var pc *PktConn + v, ok := nm.Load(raddr.String()) + if !ok && v == nil { + if c.tgtAddr == nil { + log.F("[socks5-udp] can not get target address, not a valid request") + continue + } + + lpc, nextHop, err := s.DialUDP("udp", c.tgtAddr.String()) + if err != nil { + log.F("[socks5-udp] remote dial error: %v", err) + continue + } + + pc = NewPktConn(lpc, nextHop, nil, false, nil) + nm.Store(raddr.String(), pc) + + go func() { + conn.TimedCopy(c, raddr, pc, 2*time.Minute) + pc.Close() + nm.Delete(raddr.String()) + }() + + } else { + pc = v.(*PktConn) + } + + _, err = pc.WriteTo(buf[:n], pc.writeAddr) + if err != nil { + log.F("[socks5-udp] remote write error: %v", err) + continue + } + + log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr) + } + +} + +// Handshake fast-tracks SOCKS initialization to get target address to connect. +func (s *Server) handshake(rw io.ReadWriter) (socks.Addr, error) { + // Read RFC 1928 for request and reply structure and sizes. + buf := make([]byte, socks.MaxAddrLen) + // read VER, NMETHODS, METHODS + if _, err := io.ReadFull(rw, buf[:2]); err != nil { + return nil, err + } + nmethods := buf[1] + if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil { + return nil, err + } + // write VER METHOD + if _, err := rw.Write([]byte{5, 0}); err != nil { + return nil, err + } + // read VER CMD RSV ATYP DST.ADDR DST.PORT + if _, err := io.ReadFull(rw, buf[:3]); err != nil { + return nil, err + } + cmd := buf[1] + addr, err := socks.ReadAddrBuf(rw, buf) + if err != nil { + return nil, err + } + switch cmd { + case socks.CmdConnect: + _, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) // SOCKS v5, reply succeeded + case socks.CmdUDPAssociate: + listenAddr := socks.ParseAddr(rw.(net.Conn).LocalAddr().String()) + _, err = rw.Write(append([]byte{5, 0, 0}, listenAddr...)) // SOCKS v5, reply succeeded + if err != nil { + return nil, socks.Errors[7] + } + err = socks.Errors[9] + default: + return nil, socks.Errors[7] + } + + return addr, err // skip VER, CMD, RSV fields +} diff --git a/proxy/socks5/socks5.go b/proxy/socks5/socks5.go index 3d0cf51..d9d01b2 100644 --- a/proxy/socks5/socks5.go +++ b/proxy/socks5/socks5.go @@ -12,18 +12,9 @@ package socks5 import ( - "errors" - "io" - "net" "net/url" - "strconv" - "sync" - "time" - "github.com/nadoo/glider/common/conn" "github.com/nadoo/glider/common/log" - "github.com/nadoo/glider/common/socks" - "github.com/nadoo/glider/proxy" ) // Version is socks5 version number @@ -31,20 +22,14 @@ const Version = 5 // SOCKS5 struct type SOCKS5 struct { - dialer proxy.Dialer addr string user string password string } -func init() { - proxy.RegisterDialer("socks5", NewSocks5Dialer) - proxy.RegisterServer("socks5", NewSocks5Server) -} - // NewSOCKS5 returns a Proxy that makes SOCKS v5 connections to the given address // with an optional username and password. See RFC 1928. -func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) { +func NewSOCKS5(s string) (*SOCKS5, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -56,7 +41,6 @@ func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) { pass, _ := u.User.Password() h := &SOCKS5{ - dialer: dialer, addr: addr, user: user, password: pass, @@ -64,402 +48,3 @@ func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) { return h, nil } - -// NewSocks5Dialer returns a socks5 proxy dialer. -func NewSocks5Dialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { - return NewSOCKS5(s, dialer) -} - -// NewSocks5Server returns a socks5 proxy server. -func NewSocks5Server(s string, dialer proxy.Dialer) (proxy.Server, error) { - return NewSOCKS5(s, dialer) -} - -// ListenAndServe serves socks5 requests. -func (s *SOCKS5) ListenAndServe() { - go s.ListenAndServeUDP() - s.ListenAndServeTCP() -} - -// ListenAndServeTCP . -func (s *SOCKS5) ListenAndServeTCP() { - l, err := net.Listen("tcp", s.addr) - if err != nil { - log.F("[socks5] failed to listen on %s: %v", s.addr, err) - return - } - - log.F("[socks5] listening TCP on %s", s.addr) - - for { - c, err := l.Accept() - if err != nil { - log.F("[socks5] failed to accept: %v", err) - continue - } - - go s.ServeTCP(c) - } -} - -// ServeTCP . -func (s *SOCKS5) ServeTCP(c net.Conn) { - defer c.Close() - - if c, ok := c.(*net.TCPConn); ok { - c.SetKeepAlive(true) - } - - tgt, err := s.handshake(c) - if err != nil { - // UDP: keep the connection until disconnect then free the UDP socket - if err == socks.Errors[9] { - buf := []byte{} - // block here - for { - _, err := c.Read(buf) - if err, ok := err.(net.Error); ok && err.Timeout() { - continue - } - // log.F("[socks5] servetcp udp associate end") - return - } - } - - log.F("[socks5] failed to get target address: %v", err) - return - } - - rc, err := s.dialer.Dial("tcp", tgt.String()) - if err != nil { - log.F("[socks5] failed to connect to target: %v", err) - return - } - defer rc.Close() - - log.F("[socks5] %s <-> %s", c.RemoteAddr(), tgt) - - _, _, err = conn.Relay(c, rc) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return // ignore i/o timeout - } - log.F("[socks5] relay error: %v", err) - } -} - -// ListenAndServeUDP serves udp requests. -func (s *SOCKS5) ListenAndServeUDP() { - lc, err := net.ListenPacket("udp", s.addr) - if err != nil { - log.F("[socks5-udp] failed to listen on %s: %v", s.addr, err) - return - } - defer lc.Close() - - log.F("[socks5-udp] listening UDP on %s", s.addr) - - var nm sync.Map - buf := make([]byte, conn.UDPBufSize) - - for { - c := NewPktConn(lc, nil, nil, true, nil) - - n, raddr, err := c.ReadFrom(buf) - if err != nil { - log.F("[socks5-udp] remote read error: %v", err) - continue - } - - var pc *PktConn - v, ok := nm.Load(raddr.String()) - if !ok && v == nil { - if c.tgtAddr == nil { - log.F("[socks5-udp] can not get target address, not a valid request") - continue - } - - lpc, nextHop, err := s.dialer.DialUDP("udp", c.tgtAddr.String()) - if err != nil { - log.F("[socks5-udp] remote dial error: %v", err) - continue - } - - pc = NewPktConn(lpc, nextHop, nil, false, nil) - nm.Store(raddr.String(), pc) - - go func() { - conn.TimedCopy(c, raddr, pc, 2*time.Minute) - pc.Close() - nm.Delete(raddr.String()) - }() - - } else { - pc = v.(*PktConn) - } - - _, err = pc.WriteTo(buf[:n], pc.writeAddr) - if err != nil { - log.F("[socks5-udp] remote write error: %v", err) - continue - } - - log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr) - } - -} - -// Addr returns forwarder's address -func (s *SOCKS5) Addr() string { - if s.addr == "" { - return s.dialer.Addr() - } - return s.addr -} - -// NextDialer returns the next dialer -func (s *SOCKS5) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } - -// Dial connects to the address addr on the network net via the SOCKS5 proxy. -func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) { - switch network { - case "tcp", "tcp6", "tcp4": - default: - return nil, errors.New("[socks5]: no support for connection type " + network) - } - - c, err := s.dialer.Dial(network, s.addr) - if err != nil { - log.F("dial to %s error: %s", s.addr, err) - return nil, err - } - - if err := s.connect(c, addr); err != nil { - c.Close() - return nil, err - } - - return c, nil -} - -// DialUDP connects to the given address via the proxy. -func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { - c, err := s.dialer.Dial("tcp", s.addr) - if err != nil { - log.F("[socks5] dialudp dial tcp to %s error: %s", s.addr, err) - return nil, nil, err - } - - // send VER, NMETHODS, METHODS - c.Write([]byte{5, 1, 0}) - - buf := make([]byte, socks.MaxAddrLen) - // read VER METHOD - if _, err := io.ReadFull(c, buf[:2]); err != nil { - return nil, nil, err - } - - dstAddr := socks.ParseAddr(addr) - // write VER CMD RSV ATYP DST.ADDR DST.PORT - c.Write(append([]byte{5, socks.CmdUDPAssociate, 0}, dstAddr...)) - - // read VER REP RSV ATYP BND.ADDR BND.PORT - if _, err := io.ReadFull(c, buf[:3]); err != nil { - return nil, nil, err - } - - rep := buf[1] - if rep != 0 { - log.F("[socks5] server reply: %d, not succeeded", rep) - return nil, nil, errors.New("server connect failed") - } - - uAddr, err := socks.ReadAddrBuf(c, buf) - if err != nil { - return nil, nil, err - } - - pc, nextHop, err := s.dialer.DialUDP(network, uAddr.String()) - if err != nil { - log.F("[socks5] dialudp to %s error: %s", uAddr.String(), err) - return nil, nil, err - } - - pkc := NewPktConn(pc, nextHop, dstAddr, true, c) - return pkc, nextHop, err -} - -// connect takes an existing connection to a socks5 proxy server, -// and commands the server to extend that connection to target, -// which must be a canonical address with a host and port. -func (s *SOCKS5) connect(conn net.Conn, target string) error { - host, portStr, err := net.SplitHostPort(target) - if err != nil { - return err - } - - port, err := strconv.Atoi(portStr) - if err != nil { - return errors.New("proxy: failed to parse port number: " + portStr) - } - if port < 1 || port > 0xffff { - return errors.New("proxy: port number out of range: " + portStr) - } - - // the size here is just an estimate - buf := make([]byte, 0, 6+len(host)) - - buf = append(buf, Version) - if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { - buf = append(buf, 2 /* num auth methods */, socks.AuthNone, socks.AuthPassword) - } else { - buf = append(buf, 1 /* num auth methods */, socks.AuthNone) - } - - if _, err := conn.Write(buf); err != nil { - return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - if _, err := io.ReadFull(conn, buf[:2]); err != nil { - return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - if buf[0] != 5 { - return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) - } - if buf[1] == 0xff { - return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") - } - - if buf[1] == socks.AuthPassword { - buf = buf[:0] - buf = append(buf, 1 /* password protocol version */) - buf = append(buf, uint8(len(s.user))) - buf = append(buf, s.user...) - buf = append(buf, uint8(len(s.password))) - buf = append(buf, s.password...) - - if _, err := conn.Write(buf); err != nil { - return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - if _, err := io.ReadFull(conn, buf[:2]); err != nil { - return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - if buf[1] != 0 { - return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") - } - } - - buf = buf[:0] - buf = append(buf, Version, socks.CmdConnect, 0 /* reserved */) - - if ip := net.ParseIP(host); ip != nil { - if ip4 := ip.To4(); ip4 != nil { - buf = append(buf, socks.ATypIP4) - ip = ip4 - } else { - buf = append(buf, socks.ATypIP6) - } - buf = append(buf, ip...) - } else { - if len(host) > 255 { - return errors.New("proxy: destination hostname too long: " + host) - } - buf = append(buf, socks.ATypDomain) - buf = append(buf, byte(len(host))) - buf = append(buf, host...) - } - buf = append(buf, byte(port>>8), byte(port)) - - if _, err := conn.Write(buf); err != nil { - return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - if _, err := io.ReadFull(conn, buf[:4]); err != nil { - return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - failure := "unknown error" - if int(buf[1]) < len(socks.Errors) { - failure = socks.Errors[buf[1]].Error() - } - - if len(failure) > 0 { - return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) - } - - bytesToDiscard := 0 - switch buf[3] { - case socks.ATypIP4: - bytesToDiscard = net.IPv4len - case socks.ATypIP6: - bytesToDiscard = net.IPv6len - case socks.ATypDomain: - _, err := io.ReadFull(conn, buf[:1]) - if err != nil { - return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - bytesToDiscard = int(buf[0]) - default: - return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) - } - - if cap(buf) < bytesToDiscard { - buf = make([]byte, bytesToDiscard) - } else { - buf = buf[:bytesToDiscard] - } - if _, err := io.ReadFull(conn, buf); err != nil { - return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - // Also need to discard the port number - if _, err := io.ReadFull(conn, buf[:2]); err != nil { - return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) - } - - return nil -} - -// Handshake fast-tracks SOCKS initialization to get target address to connect. -func (s *SOCKS5) handshake(rw io.ReadWriter) (socks.Addr, error) { - // Read RFC 1928 for request and reply structure and sizes. - buf := make([]byte, socks.MaxAddrLen) - // read VER, NMETHODS, METHODS - if _, err := io.ReadFull(rw, buf[:2]); err != nil { - return nil, err - } - nmethods := buf[1] - if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil { - return nil, err - } - // write VER METHOD - if _, err := rw.Write([]byte{5, 0}); err != nil { - return nil, err - } - // read VER CMD RSV ATYP DST.ADDR DST.PORT - if _, err := io.ReadFull(rw, buf[:3]); err != nil { - return nil, err - } - cmd := buf[1] - addr, err := socks.ReadAddrBuf(rw, buf) - if err != nil { - return nil, err - } - switch cmd { - case socks.CmdConnect: - _, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) // SOCKS v5, reply succeeded - case socks.CmdUDPAssociate: - listenAddr := socks.ParseAddr(rw.(net.Conn).LocalAddr().String()) - _, err = rw.Write(append([]byte{5, 0, 0}, listenAddr...)) // SOCKS v5, reply succeeded - if err != nil { - return nil, socks.Errors[7] - } - err = socks.Errors[9] - default: - return nil, socks.Errors[7] - } - - return addr, err // skip VER, CMD, RSV fields -} diff --git a/proxy/ss/dialer.go b/proxy/ss/dialer.go new file mode 100644 index 0000000..a9e89bd --- /dev/null +++ b/proxy/ss/dialer.go @@ -0,0 +1,86 @@ +package ss + +import ( + "errors" + "net" + + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/common/socks" + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterDialer("ss", CreateDialer) +} + +// Dialer struct +type Dialer struct { + *SS + dialer proxy.Dialer +} + +// NewDialer returns a proxy dialer +func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) { + h, err := NewSS(s) + if err != nil { + return nil, err + } + + d := &Dialer{SS: h, dialer: dialer} + return d, nil +} + +// CreateDialer returns a proxy dialer +func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewDialer(s, dialer) +} + +// Addr returns dialer's address +func (s *Dialer) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} + +// NextDialer returns the next dialer +func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } + +// Dial establishes a connection to the addr +func (s *Dialer) Dial(network, addr string) (net.Conn, error) { + target := socks.ParseAddr(addr) + if target == nil { + return nil, errors.New("[ss] unable to parse address: " + addr) + } + + if network == "uot" { + target[0] = target[0] | 0x8 + } + + c, err := s.dialer.Dial("tcp", s.addr) + if err != nil { + log.F("[ss] dial to %s error: %s", s.addr, err) + return nil, err + } + + c = s.StreamConn(c) + if _, err = c.Write(target); err != nil { + c.Close() + return nil, err + } + + return c, err + +} + +// DialUDP returns a PacketConn to the addr +func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { + pc, nextHop, err := s.dialer.DialUDP(network, s.addr) + if err != nil { + log.F("[ss] dialudp to %s error: %s", s.addr, err) + return nil, nil, err + } + + pkc := NewPktConn(s.PacketConn(pc), nextHop, socks.ParseAddr(addr), true) + return pkc, nextHop, err +} diff --git a/proxy/ss/server.go b/proxy/ss/server.go new file mode 100644 index 0000000..e593ca6 --- /dev/null +++ b/proxy/ss/server.go @@ -0,0 +1,194 @@ +package ss + +import ( + "net" + "sync" + "time" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/common/socks" + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterServer("ss", CreateServer) +} + +// Server struct +type Server struct { + *SS + *proxy.Forwarder +} + +// NewServer returns a local proxy server +func NewServer(s string, f *proxy.Forwarder) (*Server, error) { + h, err := NewSS(s) + if err != nil { + return nil, err + } + server := &Server{SS: h, Forwarder: f} + return server, nil +} + +// CreateServer returns a local proxy server +func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) { + return NewServer(s, f) +} + +// ListenAndServe serves requests from clients +func (s *Server) ListenAndServe() { + go s.ListenAndServeUDP() + s.ListenAndServeTCP() +} + +// ListenAndServeTCP serves tcp requests +func (s *Server) ListenAndServeTCP() { + l, err := net.Listen("tcp", s.addr) + if err != nil { + log.F("[ss] failed to listen on %s: %v", s.addr, err) + return + } + + log.F("[ss] listening TCP on %s", s.addr) + + for { + c, err := l.Accept() + if err != nil { + log.F("[ss] failed to accept: %v", err) + continue + } + go s.ServeTCP(c) + } +} + +// ServeTCP serves tcp requests +func (s *Server) ServeTCP(c net.Conn) { + defer c.Close() + + if c, ok := c.(*net.TCPConn); ok { + c.SetKeepAlive(true) + } + + c = s.StreamConn(c) + + tgt, err := socks.ReadAddr(c) + if err != nil { + log.F("[ss] failed to get target address: %v", err) + return + } + + dialer := s.NextDialer(tgt.String()) + + // udp over tcp? + uot := socks.UoT(tgt[0]) + if uot && dialer.Addr() == "DIRECT" { + rc, err := net.ListenPacket("udp", "") + if err != nil { + log.F("[ss-uottun] UDP remote listen error: %v", err) + } + defer rc.Close() + + req := make([]byte, conn.UDPBufSize) + n, err := c.Read(req) + if err != nil { + log.F("[ss-uottun] error in ioutil.ReadAll: %s\n", err) + return + } + + tgtAddr, _ := net.ResolveUDPAddr("udp", tgt.String()) + rc.WriteTo(req[:n], tgtAddr) + + buf := make([]byte, conn.UDPBufSize) + n, _, err = rc.ReadFrom(buf) + if err != nil { + log.F("[ss-uottun] read error: %v", err) + } + + c.Write(buf[:n]) + + log.F("[ss] %s <-tcp-> %s - %s <-udp-> %s ", c.RemoteAddr(), c.LocalAddr(), rc.LocalAddr(), tgt) + + return + } + + network := "tcp" + if uot { + network = "udp" + } + + rc, err := dialer.Dial(network, tgt.String()) + if err != nil { + log.F("[ss] failed to connect to target: %v", err) + return + } + defer rc.Close() + + log.F("[ss] %s <-> %s", c.RemoteAddr(), tgt) + + _, _, err = conn.Relay(c, rc) + if err != nil { + if err, ok := err.(net.Error); ok && err.Timeout() { + return // ignore i/o timeout + } + log.F("[ss] relay error: %v", err) + } + +} + +// ListenAndServeUDP serves udp requests +func (s *Server) ListenAndServeUDP() { + lc, err := net.ListenPacket("udp", s.addr) + if err != nil { + log.F("[ss-udp] failed to listen on %s: %v", s.addr, err) + return + } + defer lc.Close() + + lc = s.PacketConn(lc) + + log.F("[ss-udp] listening UDP on %s", s.addr) + + var nm sync.Map + buf := make([]byte, conn.UDPBufSize) + + for { + c := NewPktConn(lc, nil, nil, true) + + n, raddr, err := c.ReadFrom(buf) + if err != nil { + log.F("[ss-udp] remote read error: %v", err) + continue + } + + var pc *PktConn + v, ok := nm.Load(raddr.String()) + if !ok && v == nil { + lpc, nextHop, err := s.DialUDP("udp", c.tgtAddr.String()) + if err != nil { + log.F("[ss-udp] remote dial error: %v", err) + continue + } + + pc = NewPktConn(lpc, nextHop, nil, false) + nm.Store(raddr.String(), pc) + + go func() { + conn.TimedCopy(c, raddr, pc, 2*time.Minute) + pc.Close() + nm.Delete(raddr.String()) + }() + + } else { + pc = v.(*PktConn) + } + + _, err = pc.WriteTo(buf[:n], pc.writeAddr) + if err != nil { + log.F("[ss-udp] remote write error: %v", err) + continue + } + + log.F("[ss-udp] %s <-> %s", raddr, c.tgtAddr) + } +} diff --git a/proxy/ss/ss.go b/proxy/ss/ss.go index 919ac95..b8b2b63 100644 --- a/proxy/ss/ss.go +++ b/proxy/ss/ss.go @@ -1,36 +1,22 @@ package ss import ( - "errors" - "net" "net/url" "strings" - "sync" - "time" "github.com/shadowsocks/go-shadowsocks2/core" - "github.com/nadoo/glider/common/conn" "github.com/nadoo/glider/common/log" - "github.com/nadoo/glider/common/socks" - "github.com/nadoo/glider/proxy" ) // SS . type SS struct { - dialer proxy.Dialer - addr string - + addr string core.Cipher } -func init() { - proxy.RegisterDialer("ss", NewSSDialer) - proxy.RegisterServer("ss", NewSSServer) -} - // NewSS returns a shadowsocks proxy. -func NewSS(s string, dialer proxy.Dialer) (*SS, error) { +func NewSS(s string) (*SS, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -47,7 +33,6 @@ func NewSS(s string, dialer proxy.Dialer) (*SS, error) { } p := &SS{ - dialer: dialer, addr: addr, Cipher: ciph, } @@ -55,224 +40,7 @@ func NewSS(s string, dialer proxy.Dialer) (*SS, error) { return p, nil } -// NewSSDialer returns a ss proxy dialer. -func NewSSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { - return NewSS(s, dialer) -} - -// NewSSServer returns a ss proxy server. -func NewSSServer(s string, dialer proxy.Dialer) (proxy.Server, error) { - return NewSS(s, dialer) -} - -// ListenAndServe serves ss requests. -func (s *SS) ListenAndServe() { - go s.ListenAndServeUDP() - s.ListenAndServeTCP() -} - -// ListenAndServeTCP serves tcp ss requests. -func (s *SS) ListenAndServeTCP() { - l, err := net.Listen("tcp", s.addr) - if err != nil { - log.F("[ss] failed to listen on %s: %v", s.addr, err) - return - } - - log.F("[ss] listening TCP on %s", s.addr) - - for { - c, err := l.Accept() - if err != nil { - log.F("[ss] failed to accept: %v", err) - continue - } - go s.ServeTCP(c) - } -} - -// ServeTCP serves tcp ss requests. -func (s *SS) ServeTCP(c net.Conn) { - defer c.Close() - - if c, ok := c.(*net.TCPConn); ok { - c.SetKeepAlive(true) - } - - c = s.StreamConn(c) - - tgt, err := socks.ReadAddr(c) - if err != nil { - log.F("[ss] failed to get target address: %v", err) - return - } - - dialer := s.dialer.NextDialer(tgt.String()) - - // udp over tcp? - uot := socks.UoT(tgt[0]) - if uot && dialer.Addr() == "DIRECT" { - rc, err := net.ListenPacket("udp", "") - if err != nil { - log.F("[ss-uottun] UDP remote listen error: %v", err) - } - defer rc.Close() - - req := make([]byte, conn.UDPBufSize) - n, err := c.Read(req) - if err != nil { - log.F("[ss-uottun] error in ioutil.ReadAll: %s\n", err) - return - } - - tgtAddr, _ := net.ResolveUDPAddr("udp", tgt.String()) - rc.WriteTo(req[:n], tgtAddr) - - buf := make([]byte, conn.UDPBufSize) - n, _, err = rc.ReadFrom(buf) - if err != nil { - log.F("[ss-uottun] read error: %v", err) - } - - c.Write(buf[:n]) - - log.F("[ss] %s <-tcp-> %s - %s <-udp-> %s ", c.RemoteAddr(), c.LocalAddr(), rc.LocalAddr(), tgt) - - return - } - - network := "tcp" - if uot { - network = "udp" - } - - rc, err := dialer.Dial(network, tgt.String()) - if err != nil { - log.F("[ss] failed to connect to target: %v", err) - return - } - defer rc.Close() - - log.F("[ss] %s <-> %s", c.RemoteAddr(), tgt) - - _, _, err = conn.Relay(c, rc) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return // ignore i/o timeout - } - log.F("[ss] relay error: %v", err) - } - -} - -// ListenAndServeUDP serves udp ss requests. -func (s *SS) ListenAndServeUDP() { - lc, err := net.ListenPacket("udp", s.addr) - if err != nil { - log.F("[ss-udp] failed to listen on %s: %v", s.addr, err) - return - } - defer lc.Close() - - lc = s.PacketConn(lc) - - log.F("[ss-udp] listening UDP on %s", s.addr) - - var nm sync.Map - buf := make([]byte, conn.UDPBufSize) - - for { - c := NewPktConn(lc, nil, nil, true) - - n, raddr, err := c.ReadFrom(buf) - if err != nil { - log.F("[ss-udp] remote read error: %v", err) - continue - } - - var pc *PktConn - v, ok := nm.Load(raddr.String()) - if !ok && v == nil { - lpc, nextHop, err := s.dialer.DialUDP("udp", c.tgtAddr.String()) - if err != nil { - log.F("[ss-udp] remote dial error: %v", err) - continue - } - - pc = NewPktConn(lpc, nextHop, nil, false) - nm.Store(raddr.String(), pc) - - go func() { - conn.TimedCopy(c, raddr, pc, 2*time.Minute) - pc.Close() - nm.Delete(raddr.String()) - }() - - } else { - pc = v.(*PktConn) - } - - _, err = pc.WriteTo(buf[:n], pc.writeAddr) - if err != nil { - log.F("[ss-udp] remote write error: %v", err) - continue - } - - log.F("[ss-udp] %s <-> %s", raddr, c.tgtAddr) - } -} - // ListCipher . func ListCipher() string { return strings.Join(core.ListCipher(), " ") } - -// Addr returns forwarder's address -func (s *SS) Addr() string { - if s.addr == "" { - return s.dialer.Addr() - } - return s.addr -} - -// NextDialer returns the next dialer -func (s *SS) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } - -// Dial connects to the address addr on the network net via the proxy. -func (s *SS) Dial(network, addr string) (net.Conn, error) { - target := socks.ParseAddr(addr) - if target == nil { - return nil, errors.New("[ss] unable to parse address: " + addr) - } - - if network == "uot" { - target[0] = target[0] | 0x8 - } - - c, err := s.dialer.Dial("tcp", s.addr) - if err != nil { - log.F("[ss] dial to %s error: %s", s.addr, err) - return nil, err - } - - c = s.StreamConn(c) - if _, err = c.Write(target); err != nil { - c.Close() - return nil, err - } - - return c, err - -} - -// DialUDP connects to the given address via the proxy. -func (s *SS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { - pc, nextHop, err := s.dialer.DialUDP(network, s.addr) - if err != nil { - log.F("[ss] dialudp to %s error: %s", s.addr, err) - return nil, nil, err - } - - pkc := NewPktConn(s.PacketConn(pc), nextHop, socks.ParseAddr(addr), true) - return pkc, nextHop, err -} diff --git a/proxy/ssr/dialer.go b/proxy/ssr/dialer.go new file mode 100644 index 0000000..fbe29e0 --- /dev/null +++ b/proxy/ssr/dialer.go @@ -0,0 +1,130 @@ +package ssr + +import ( + "errors" + "net" + "strconv" + "strings" + + shadowsocksr "github.com/sun8911879/shadowsocksR" + "github.com/sun8911879/shadowsocksR/obfs" + "github.com/sun8911879/shadowsocksR/protocol" + "github.com/sun8911879/shadowsocksR/ssr" + + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/common/socks" + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterDialer("ssr", CreateDialer) +} + +// Dialer struct +type Dialer struct { + *SSR + dialer proxy.Dialer +} + +// NewDialer returns a proxy dialer +func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) { + h, err := NewSSR(s) + if err != nil { + return nil, err + } + + d := &Dialer{SSR: h, dialer: dialer} + return d, nil +} + +// CreateDialer returns a proxy dialer +func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewDialer(s, dialer) +} + +// Addr returns dialer's address +func (s *Dialer) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} + +// NextDialer returns the next dialer +func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } + +// Dial establishes a connection to the addr +func (s *Dialer) Dial(network, addr string) (net.Conn, error) { + target := socks.ParseAddr(addr) + if target == nil { + return nil, errors.New("[ssr] unable to parse address: " + addr) + } + + cipher, err := shadowsocksr.NewStreamCipher(s.EncryptMethod, s.EncryptPassword) + if err != nil { + return nil, err + } + + c, err := s.dialer.Dial("tcp", s.addr) + if err != nil { + log.F("[ssr] dial to %s error: %s", s.addr, err) + return nil, err + } + + ssrconn := shadowsocksr.NewSSTCPConn(c, cipher) + if ssrconn.Conn == nil || ssrconn.RemoteAddr() == nil { + return nil, errors.New("[ssr] nil connection") + } + + // should initialize obfs/protocol now + rs := strings.Split(ssrconn.RemoteAddr().String(), ":") + port, _ := strconv.Atoi(rs[1]) + + ssrconn.IObfs = obfs.NewObfs(s.Obfs) + if ssrconn.IObfs == nil { + return nil, errors.New("[ssr] unsupported obfs type: " + s.Obfs) + } + + obfsServerInfo := &ssr.ServerInfoForObfs{ + Host: rs[0], + Port: uint16(port), + TcpMss: 1460, + Param: s.ObfsParam, + } + ssrconn.IObfs.SetServerInfo(obfsServerInfo) + + ssrconn.IProtocol = protocol.NewProtocol(s.Protocol) + if ssrconn.IProtocol == nil { + return nil, errors.New("[ssr] unsupported protocol type: " + s.Protocol) + } + + protocolServerInfo := &ssr.ServerInfoForObfs{ + Host: rs[0], + Port: uint16(port), + TcpMss: 1460, + Param: s.ProtocolParam, + } + ssrconn.IProtocol.SetServerInfo(protocolServerInfo) + + if s.ObfsData == nil { + s.ObfsData = ssrconn.IObfs.GetData() + } + ssrconn.IObfs.SetData(s.ObfsData) + + if s.ProtocolData == nil { + s.ProtocolData = ssrconn.IProtocol.GetData() + } + ssrconn.IProtocol.SetData(s.ProtocolData) + + if _, err := ssrconn.Write(target); err != nil { + ssrconn.Close() + return nil, err + } + + return ssrconn, err +} + +// DialUDP returns a PacketConn to the addr +func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { + return nil, nil, errors.New("[ssr] udp not supported now") +} diff --git a/proxy/ssr/ssr.go b/proxy/ssr/ssr.go index 216cae0..c392174 100644 --- a/proxy/ssr/ssr.go +++ b/proxy/ssr/ssr.go @@ -1,26 +1,14 @@ package ssr import ( - "errors" - "net" "net/url" - "strconv" - "strings" - - shadowsocksr "github.com/sun8911879/shadowsocksR" - "github.com/sun8911879/shadowsocksR/obfs" - "github.com/sun8911879/shadowsocksR/protocol" - "github.com/sun8911879/shadowsocksR/ssr" "github.com/nadoo/glider/common/log" - "github.com/nadoo/glider/common/socks" - "github.com/nadoo/glider/proxy" ) // SSR . type SSR struct { - dialer proxy.Dialer - addr string + addr string EncryptMethod string EncryptPassword string @@ -32,12 +20,8 @@ type SSR struct { ProtocolData interface{} } -func init() { - proxy.RegisterDialer("ssr", NewSSRDialer) -} - // NewSSR returns a shadowsocksr proxy, ssr://method:pass@host:port/query -func NewSSR(s string, dialer proxy.Dialer) (*SSR, error) { +func NewSSR(s string) (*SSR, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -49,7 +33,6 @@ func NewSSR(s string, dialer proxy.Dialer) (*SSR, error) { pass, _ := u.User.Password() p := &SSR{ - dialer: dialer, addr: addr, EncryptMethod: method, EncryptPassword: pass, @@ -63,95 +46,3 @@ func NewSSR(s string, dialer proxy.Dialer) (*SSR, error) { return p, nil } - -// NewSSRDialer returns a ssr proxy dialer. -func NewSSRDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { - return NewSSR(s, dialer) -} - -// Addr returns forwarder's address -func (s *SSR) Addr() string { - if s.addr == "" { - return s.dialer.Addr() - } - return s.addr -} - -// NextDialer returns the next dialer -func (s *SSR) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } - -// Dial connects to the address addr on the network net via the proxy. -func (s *SSR) Dial(network, addr string) (net.Conn, error) { - target := socks.ParseAddr(addr) - if target == nil { - return nil, errors.New("[ssr] unable to parse address: " + addr) - } - - cipher, err := shadowsocksr.NewStreamCipher(s.EncryptMethod, s.EncryptPassword) - if err != nil { - return nil, err - } - - c, err := s.dialer.Dial("tcp", s.addr) - if err != nil { - log.F("[ssr] dial to %s error: %s", s.addr, err) - return nil, err - } - - ssrconn := shadowsocksr.NewSSTCPConn(c, cipher) - if ssrconn.Conn == nil || ssrconn.RemoteAddr() == nil { - return nil, errors.New("[ssr] nil connection") - } - - // should initialize obfs/protocol now - rs := strings.Split(ssrconn.RemoteAddr().String(), ":") - port, _ := strconv.Atoi(rs[1]) - - ssrconn.IObfs = obfs.NewObfs(s.Obfs) - if ssrconn.IObfs == nil { - return nil, errors.New("[ssr] unsupported obfs type: " + s.Obfs) - } - - obfsServerInfo := &ssr.ServerInfoForObfs{ - Host: rs[0], - Port: uint16(port), - TcpMss: 1460, - Param: s.ObfsParam, - } - ssrconn.IObfs.SetServerInfo(obfsServerInfo) - - ssrconn.IProtocol = protocol.NewProtocol(s.Protocol) - if ssrconn.IProtocol == nil { - return nil, errors.New("[ssr] unsupported protocol type: " + s.Protocol) - } - - protocolServerInfo := &ssr.ServerInfoForObfs{ - Host: rs[0], - Port: uint16(port), - TcpMss: 1460, - Param: s.ProtocolParam, - } - ssrconn.IProtocol.SetServerInfo(protocolServerInfo) - - if s.ObfsData == nil { - s.ObfsData = ssrconn.IObfs.GetData() - } - ssrconn.IObfs.SetData(s.ObfsData) - - if s.ProtocolData == nil { - s.ProtocolData = ssrconn.IProtocol.GetData() - } - ssrconn.IProtocol.SetData(s.ProtocolData) - - if _, err := ssrconn.Write(target); err != nil { - ssrconn.Close() - return nil, err - } - - return ssrconn, err -} - -// DialUDP connects to the given address via the proxy. -func (s *SSR) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { - return nil, nil, errors.New("[ssr] udp not supported now") -} diff --git a/proxy/tcptun/server.go b/proxy/tcptun/server.go new file mode 100644 index 0000000..6964427 --- /dev/null +++ b/proxy/tcptun/server.go @@ -0,0 +1,80 @@ +package tcptun + +import ( + "net" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterServer("tcptun", CreateServer) +} + +// Server struct +type Server struct { + *TCPTun + *proxy.Forwarder +} + +// NewServer returns a local proxy server +func NewServer(s string, f *proxy.Forwarder) (*Server, error) { + h, err := NewTCPTun(s) + if err != nil { + return nil, err + } + server := &Server{TCPTun: h, Forwarder: f} + return server, nil +} + +// CreateServer returns a local proxy server +func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) { + return NewServer(s, f) +} + +// ListenAndServe serves requests from clients +func (s *Server) ListenAndServe() { + l, err := net.Listen("tcp", s.addr) + if err != nil { + log.F("failed to listen on %s: %v", s.addr, err) + return + } + + log.F("listening TCP on %s", s.addr) + + for { + c, err := l.Accept() + if err != nil { + log.F("failed to accept: %v", err) + continue + } + + go func() { + defer c.Close() + + if c, ok := c.(*net.TCPConn); ok { + c.SetKeepAlive(true) + } + + rc, err := s.Dial("tcp", s.raddr) + if err != nil { + + log.F("failed to connect to target: %v", err) + return + } + defer rc.Close() + + log.F("[tcptun] %s <-> %s", c.RemoteAddr(), s.raddr) + + _, _, err = conn.Relay(c, rc) + if err != nil { + if err, ok := err.(net.Error); ok && err.Timeout() { + return // ignore i/o timeout + } + log.F("relay error: %v", err) + } + + }() + } +} diff --git a/proxy/tcptun/tcptun.go b/proxy/tcptun/tcptun.go index 2f2a6c7..abbc409 100644 --- a/proxy/tcptun/tcptun.go +++ b/proxy/tcptun/tcptun.go @@ -1,29 +1,20 @@ package tcptun import ( - "net" "net/url" "strings" - "github.com/nadoo/glider/common/conn" "github.com/nadoo/glider/common/log" - "github.com/nadoo/glider/proxy" ) // TCPTun struct type TCPTun struct { - dialer proxy.Dialer - addr string - + addr string raddr string } -func init() { - proxy.RegisterServer("tcptun", NewTCPTunServer) -} - // NewTCPTun returns a tcptun proxy. -func NewTCPTun(s string, dialer proxy.Dialer) (*TCPTun, error) { +func NewTCPTun(s string) (*TCPTun, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -34,61 +25,9 @@ func NewTCPTun(s string, dialer proxy.Dialer) (*TCPTun, error) { d := strings.Split(addr, "=") p := &TCPTun{ - dialer: dialer, - addr: d[0], - raddr: d[1], + addr: d[0], + raddr: d[1], } return p, nil } - -// NewTCPTunServer returns a udp tunnel server. -func NewTCPTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) { - return NewTCPTun(s, dialer) -} - -// ListenAndServe . -func (s *TCPTun) ListenAndServe() { - l, err := net.Listen("tcp", s.addr) - if err != nil { - log.F("failed to listen on %s: %v", s.addr, err) - return - } - - log.F("listening TCP on %s", s.addr) - - for { - c, err := l.Accept() - if err != nil { - log.F("failed to accept: %v", err) - continue - } - - go func() { - defer c.Close() - - if c, ok := c.(*net.TCPConn); ok { - c.SetKeepAlive(true) - } - - rc, err := s.dialer.Dial("tcp", s.raddr) - if err != nil { - - log.F("failed to connect to target: %v", err) - return - } - defer rc.Close() - - log.F("[tcptun] %s <-> %s", c.RemoteAddr(), s.raddr) - - _, _, err = conn.Relay(c, rc) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return // ignore i/o timeout - } - log.F("relay error: %v", err) - } - - }() - } -} diff --git a/proxy/tls/dialer.go b/proxy/tls/dialer.go new file mode 100644 index 0000000..fbec203 --- /dev/null +++ b/proxy/tls/dialer.go @@ -0,0 +1,65 @@ +package tls + +import ( + stdtls "crypto/tls" + "errors" + "net" + + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterDialer("tls", CreateDialer) +} + +// Dialer struct +type Dialer struct { + *TLS + dialer proxy.Dialer +} + +// NewDialer returns a proxy dialer +func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) { + h, err := NewTLS(s) + if err != nil { + return nil, err + } + + d := &Dialer{TLS: h, dialer: dialer} + return d, nil +} + +// CreateDialer returns a proxy dialer +func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewDialer(s, dialer) +} + +// Addr returns dialer's address +func (s *Dialer) Addr() string { return s.addr } + +// NextDialer returns the next dialer +func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } + +// Dial establishes a connection to the addr +func (s *Dialer) Dial(network, addr string) (net.Conn, error) { + cc, err := s.dialer.Dial("tcp", s.addr) + if err != nil { + log.F("[tls] dial to %s error: %s", s.addr, err) + return nil, err + } + + conf := &stdtls.Config{ + ServerName: s.serverName, + InsecureSkipVerify: s.skipVerify, + } + + c := stdtls.Client(cc, conf) + err = c.Handshake() + return c, err +} + +// DialUDP returns a PacketConn to the addr +func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { + return nil, nil, errors.New("tls client does not support udp now") +} diff --git a/proxy/tls/tls.go b/proxy/tls/tls.go index d4f8cab..9dda7f5 100644 --- a/proxy/tls/tls.go +++ b/proxy/tls/tls.go @@ -1,31 +1,22 @@ package tls import ( - stdtls "crypto/tls" - "errors" - "net" "net/url" "strings" "github.com/nadoo/glider/common/log" - "github.com/nadoo/glider/proxy" ) // TLS . type TLS struct { - dialer proxy.Dialer - addr string + addr string serverName string skipVerify bool } -func init() { - proxy.RegisterDialer("tls", NewTLSDialer) -} - // NewTLS returns a tls proxy. -func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) { +func NewTLS(s string) (*TLS, error) { u, err := url.Parse(s) if err != nil { log.F("parse url err: %s", err) @@ -44,7 +35,6 @@ func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) { serverName := addr[:colonPos] p := &TLS{ - dialer: dialer, addr: addr, serverName: serverName, skipVerify: false, @@ -56,37 +46,3 @@ func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) { return p, nil } - -// NewTLSDialer returns a tls proxy dialer. -func NewTLSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { - return NewTLS(s, dialer) -} - -// Addr returns forwarder's address -func (s *TLS) Addr() string { return s.addr } - -// NextDialer returns the next dialer -func (s *TLS) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } - -// Dial connects to the address addr on the network net via the proxy. -func (s *TLS) Dial(network, addr string) (net.Conn, error) { - cc, err := s.dialer.Dial("tcp", s.addr) - if err != nil { - log.F("[tls] dial to %s error: %s", s.addr, err) - return nil, err - } - - conf := &stdtls.Config{ - ServerName: s.serverName, - InsecureSkipVerify: s.skipVerify, - } - - c := stdtls.Client(cc, conf) - err = c.Handshake() - return c, err -} - -// DialUDP connects to the given address via the proxy. -func (s *TLS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { - return nil, nil, errors.New("tls client does not support udp now") -} diff --git a/proxy/tproxy/tproxy_linux.go b/proxy/tproxy/server_linux.go similarity index 88% rename from proxy/tproxy/tproxy_linux.go rename to proxy/tproxy/server_linux.go index 6398eb4..14b9265 100644 --- a/proxy/tproxy/tproxy_linux.go +++ b/proxy/tproxy/server_linux.go @@ -17,18 +17,18 @@ import ( "github.com/nadoo/glider/proxy" ) -// TProxy struct -type TProxy struct { - dialer proxy.Dialer - addr string -} - func init() { - proxy.RegisterServer("tproxy", NewTProxyServer) + proxy.RegisterServer("tproxy", CreateServer) } -// NewTProxy returns a tproxy. -func NewTProxy(s string, dialer proxy.Dialer) (*TProxy, error) { +// Server struct +type Server struct { + addr string + *proxy.Forwarder +} + +// NewServer returns a local proxy server +func NewServer(s string, f *proxy.Forwarder) (*Server, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -37,32 +37,32 @@ func NewTProxy(s string, dialer proxy.Dialer) (*TProxy, error) { addr := u.Host - p := &TProxy{ - dialer: dialer, - addr: addr, + p := &Server{ + addr: addr, + Forwarder: f, } return p, nil } -// NewTProxyServer returns a udp tunnel server. -func NewTProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) { - return NewTProxy(s, dialer) +// CreateServer returns a local proxy server +func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) { + return NewServer(s, f) } // ListenAndServe . -func (s *TProxy) ListenAndServe() { +func (s *Server) ListenAndServe() { // go s.ListenAndServeTCP() s.ListenAndServeUDP() } // ListenAndServeTCP . -func (s *TProxy) ListenAndServeTCP() { +func (s *Server) ListenAndServeTCP() { log.F("[tproxy] tcp mode not supported now, please use 'redir' instead") } // ListenAndServeUDP . -func (s *TProxy) ListenAndServeUDP() { +func (s *Server) ListenAndServeUDP() { laddr, err := net.ResolveUDPAddr("udp", s.addr) if err != nil { log.F("[tproxy] failed to resolve addr %s: %v", s.addr, err) diff --git a/proxy/udptun/server.go b/proxy/udptun/server.go new file mode 100644 index 0000000..5819c9d --- /dev/null +++ b/proxy/udptun/server.go @@ -0,0 +1,92 @@ +package udptun + +import ( + "net" + "sync" + "time" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterServer("udptun", CreateServer) +} + +// Server struct +type Server struct { + *UDPTun + *proxy.Forwarder +} + +// NewServer returns a local proxy server +func NewServer(s string, f *proxy.Forwarder) (*Server, error) { + h, err := NewUDPTun(s) + if err != nil { + return nil, err + } + server := &Server{UDPTun: h, Forwarder: f} + return server, nil +} + +// CreateServer returns a local proxy server +func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) { + return NewServer(s, f) +} + +// ListenAndServe serves requests from clients +func (s *Server) ListenAndServe() { + c, err := net.ListenPacket("udp", s.addr) + if err != nil { + log.F("[udptun] failed to listen on %s: %v", s.addr, err) + return + } + defer c.Close() + + log.F("[udptun] listening UDP on %s", s.addr) + + var nm sync.Map + buf := make([]byte, conn.UDPBufSize) + + for { + n, raddr, err := c.ReadFrom(buf) + if err != nil { + log.F("[udptun] read error: %v", err) + continue + } + + var pc net.PacketConn + var writeAddr net.Addr + + v, ok := nm.Load(raddr.String()) + if !ok && v == nil { + + pc, writeAddr, err = s.DialUDP("udp", s.raddr) + if err != nil { + log.F("[udptun] remote dial error: %v", err) + continue + } + + nm.Store(raddr.String(), pc) + + go func() { + conn.TimedCopy(c, raddr, pc, 2*time.Minute) + pc.Close() + nm.Delete(raddr.String()) + }() + + } else { + pc = v.(net.PacketConn) + } + + _, err = pc.WriteTo(buf[:n], writeAddr) + if err != nil { + log.F("[udptun] remote write error: %v", err) + continue + } + + log.F("[udptun] %s <-> %s", raddr, s.raddr) + + } +} diff --git a/proxy/udptun/udptun.go b/proxy/udptun/udptun.go index 8fb72ff..60afe10 100644 --- a/proxy/udptun/udptun.go +++ b/proxy/udptun/udptun.go @@ -1,31 +1,20 @@ package udptun import ( - "net" "net/url" "strings" - "sync" - "time" - "github.com/nadoo/glider/common/conn" "github.com/nadoo/glider/common/log" - "github.com/nadoo/glider/proxy" ) // UDPTun struct type UDPTun struct { - dialer proxy.Dialer - addr string - + addr string raddr string } -func init() { - proxy.RegisterServer("udptun", NewUDPTunServer) -} - // NewUDPTun returns a UDPTun proxy. -func NewUDPTun(s string, dialer proxy.Dialer) (*UDPTun, error) { +func NewUDPTun(s string) (*UDPTun, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -36,71 +25,9 @@ func NewUDPTun(s string, dialer proxy.Dialer) (*UDPTun, error) { d := strings.Split(addr, "=") p := &UDPTun{ - dialer: dialer, - addr: d[0], - raddr: d[1], + addr: d[0], + raddr: d[1], } return p, nil } - -// NewUDPTunServer returns a udp tunnel server. -func NewUDPTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) { - return NewUDPTun(s, dialer) -} - -// ListenAndServe . -func (s *UDPTun) ListenAndServe() { - c, err := net.ListenPacket("udp", s.addr) - if err != nil { - log.F("[udptun] failed to listen on %s: %v", s.addr, err) - return - } - defer c.Close() - - log.F("[udptun] listening UDP on %s", s.addr) - - var nm sync.Map - buf := make([]byte, conn.UDPBufSize) - - for { - n, raddr, err := c.ReadFrom(buf) - if err != nil { - log.F("[udptun] read error: %v", err) - continue - } - - var pc net.PacketConn - var writeAddr net.Addr - - v, ok := nm.Load(raddr.String()) - if !ok && v == nil { - - pc, writeAddr, err = s.dialer.DialUDP("udp", s.raddr) - if err != nil { - log.F("[udptun] remote dial error: %v", err) - continue - } - - nm.Store(raddr.String(), pc) - - go func() { - conn.TimedCopy(c, raddr, pc, 2*time.Minute) - pc.Close() - nm.Delete(raddr.String()) - }() - - } else { - pc = v.(net.PacketConn) - } - - _, err = pc.WriteTo(buf[:n], writeAddr) - if err != nil { - log.F("[udptun] remote write error: %v", err) - continue - } - - log.F("[udptun] %s <-> %s", raddr, s.raddr) - - } -} diff --git a/proxy/uottun/server.go b/proxy/uottun/server.go new file mode 100644 index 0000000..eb0bddf --- /dev/null +++ b/proxy/uottun/server.go @@ -0,0 +1,90 @@ +package uottun + +import ( + "io/ioutil" + "net" + "time" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterServer("uottun", CreateServer) +} + +// Server struct +type Server struct { + *UoTTun + *proxy.Forwarder +} + +// NewServer returns a local proxy server +func NewServer(s string, f *proxy.Forwarder) (*Server, error) { + h, err := NewUoTTun(s) + if err != nil { + return nil, err + } + server := &Server{UoTTun: h, Forwarder: f} + return server, nil +} + +// CreateServer returns a local proxy server +func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) { + return NewServer(s, f) +} + +// ListenAndServe serves requests from clients +func (s *Server) ListenAndServe() { + c, err := net.ListenPacket("udp", s.addr) + if err != nil { + log.F("[uottun] failed to listen on %s: %v", s.addr, err) + return + } + defer c.Close() + + log.F("[uottun] listening UDP on %s", s.addr) + + buf := make([]byte, conn.UDPBufSize) + + for { + n, clientAddr, err := c.ReadFrom(buf) + if err != nil { + log.F("[uottun] read error: %v", err) + continue + } + + rc, err := s.Dial("uot", s.raddr) + if err != nil { + log.F("[uottun] failed to connect to server %v: %v", s.raddr, err) + continue + } + + go func() { + // no remote forwarder, just a local udp forwarder + if urc, ok := rc.(*net.UDPConn); ok { + conn.TimedCopy(c, clientAddr, urc, 2*time.Minute) + urc.Close() + return + } + + // remote forwarder, udp over tcp + resp, err := ioutil.ReadAll(rc) + if err != nil { + log.F("error in ioutil.ReadAll: %s\n", err) + return + } + rc.Close() + c.WriteTo(resp, clientAddr) + }() + + _, err = rc.Write(buf[:n]) + if err != nil { + log.F("[uottun] remote write error: %v", err) + continue + } + + log.F("[uottun] %s <-> %s", clientAddr, s.raddr) + } +} diff --git a/proxy/uottun/uottun.go b/proxy/uottun/uottun.go index f43418c..83142ba 100644 --- a/proxy/uottun/uottun.go +++ b/proxy/uottun/uottun.go @@ -1,31 +1,20 @@ package uottun import ( - "io/ioutil" - "net" "net/url" "strings" - "time" - "github.com/nadoo/glider/common/conn" "github.com/nadoo/glider/common/log" - "github.com/nadoo/glider/proxy" ) // UoTTun udp over tcp tunnel type UoTTun struct { - dialer proxy.Dialer - addr string - + addr string raddr string } -func init() { - proxy.RegisterServer("uottun", NewUoTTunServer) -} - // NewUoTTun returns a UoTTun proxy. -func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) { +func NewUoTTun(s string) (*UoTTun, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -36,69 +25,9 @@ func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) { d := strings.Split(addr, "=") p := &UoTTun{ - dialer: dialer, - addr: d[0], - raddr: d[1], + addr: d[0], + raddr: d[1], } return p, nil } - -// NewUoTTunServer returns a uot tunnel server. -func NewUoTTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) { - return NewUoTTun(s, dialer) -} - -// ListenAndServe . -func (s *UoTTun) ListenAndServe() { - c, err := net.ListenPacket("udp", s.addr) - if err != nil { - log.F("[uottun] failed to listen on %s: %v", s.addr, err) - return - } - defer c.Close() - - log.F("[uottun] listening UDP on %s", s.addr) - - buf := make([]byte, conn.UDPBufSize) - - for { - n, clientAddr, err := c.ReadFrom(buf) - if err != nil { - log.F("[uottun] read error: %v", err) - continue - } - - rc, err := s.dialer.Dial("uot", s.raddr) - if err != nil { - log.F("[uottun] failed to connect to server %v: %v", s.raddr, err) - continue - } - - go func() { - // no remote forwarder, just a local udp forwarder - if urc, ok := rc.(*net.UDPConn); ok { - conn.TimedCopy(c, clientAddr, urc, 2*time.Minute) - urc.Close() - return - } - - // remote forwarder, udp over tcp - resp, err := ioutil.ReadAll(rc) - if err != nil { - log.F("error in ioutil.ReadAll: %s\n", err) - return - } - rc.Close() - c.WriteTo(resp, clientAddr) - }() - - _, err = rc.Write(buf[:n]) - if err != nil { - log.F("[uottun] remote write error: %v", err) - continue - } - - log.F("[uottun] %s <-> %s", clientAddr, s.raddr) - } -} diff --git a/proxy/vmess/dialer.go b/proxy/vmess/dialer.go new file mode 100644 index 0000000..5f875c9 --- /dev/null +++ b/proxy/vmess/dialer.go @@ -0,0 +1,60 @@ +package vmess + +import ( + "errors" + "net" + + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterDialer("vmess", CreateDialer) +} + +// Dialer struct +type Dialer struct { + *VMess + dialer proxy.Dialer +} + +// NewDialer returns a proxy dialer +func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) { + h, err := NewVMess(s) + if err != nil { + return nil, err + } + + d := &Dialer{VMess: h, dialer: dialer} + return d, nil +} + +// CreateDialer returns a proxy dialer +func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewDialer(s, dialer) +} + +// Addr returns dialer's address +func (s *Dialer) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} + +// NextDialer returns the next dialer +func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } + +// Dial establishes a connection to the addr +func (s *Dialer) Dial(network, addr string) (net.Conn, error) { + rc, err := s.dialer.Dial("tcp", s.addr) + if err != nil { + return nil, err + } + + return s.client.NewConn(rc, addr) +} + +// DialUDP returns a PacketConn to the addr +func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { + return nil, nil, errors.New("vmess client does not support udp now") +} diff --git a/proxy/vmess/vmess.go b/proxy/vmess/vmess.go index 6e46481..ccc1068 100644 --- a/proxy/vmess/vmess.go +++ b/proxy/vmess/vmess.go @@ -1,19 +1,15 @@ package vmess import ( - "errors" - "net" "net/url" "strconv" "github.com/nadoo/glider/common/log" - "github.com/nadoo/glider/proxy" ) // VMess . type VMess struct { - dialer proxy.Dialer - addr string + addr string uuid string alterID int @@ -22,12 +18,8 @@ type VMess struct { client *Client } -func init() { - proxy.RegisterDialer("vmess", NewVMessDialer) -} - // NewVMess returns a vmess proxy. -func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) { +func NewVMess(s string) (*VMess, error) { u, err := url.Parse(s) if err != nil { log.F("parse url err: %s", err) @@ -62,7 +54,6 @@ func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) { } p := &VMess{ - dialer: dialer, addr: addr, uuid: uuid, alterID: int(alterID), @@ -72,34 +63,3 @@ func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) { return p, nil } - -// NewVMessDialer returns a vmess proxy dialer. -func NewVMessDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { - return NewVMess(s, dialer) -} - -// Addr returns forwarder's address -func (s *VMess) Addr() string { - if s.addr == "" { - return s.dialer.Addr() - } - return s.addr -} - -// NextDialer returns the next dialer -func (s *VMess) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } - -// Dial connects to the address addr on the network net via the proxy. -func (s *VMess) Dial(network, addr string) (net.Conn, error) { - rc, err := s.dialer.Dial("tcp", s.addr) - if err != nil { - return nil, err - } - - return s.client.NewConn(rc, addr) -} - -// DialUDP connects to the given address via the proxy. -func (s *VMess) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { - return nil, nil, errors.New("vmess client does not support udp now") -} diff --git a/proxy/ws/dialer.go b/proxy/ws/dialer.go new file mode 100644 index 0000000..ec39b8f --- /dev/null +++ b/proxy/ws/dialer.go @@ -0,0 +1,60 @@ +package ws + +import ( + "errors" + "net" + + "github.com/nadoo/glider/proxy" +) + +func init() { + proxy.RegisterDialer("ws", CreateDialer) +} + +// Dialer struct +type Dialer struct { + *WS + dialer proxy.Dialer +} + +// NewDialer returns a proxy dialer +func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) { + h, err := NewWS(s, dialer) + if err != nil { + return nil, err + } + + d := &Dialer{WS: h, dialer: dialer} + return d, nil +} + +// CreateDialer returns a proxy dialer +func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewDialer(s, dialer) +} + +// Addr returns dialer's address +func (s *Dialer) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} + +// NextDialer returns the next dialer +func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } + +// Dial establishes a connection to the addr +func (s *Dialer) Dial(network, addr string) (net.Conn, error) { + rc, err := s.dialer.Dial("tcp", s.addr) + if err != nil { + return nil, err + } + + return s.client.NewConn(rc, addr) +} + +// DialUDP returns a PacketConn to the addr +func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { + return nil, nil, errors.New("ws client does not support udp now") +} diff --git a/proxy/ws/ws.go b/proxy/ws/ws.go index 3f6bf48..f07cd5d 100644 --- a/proxy/ws/ws.go +++ b/proxy/ws/ws.go @@ -1,8 +1,6 @@ package ws import ( - "errors" - "net" "net/url" "strings" @@ -12,16 +10,10 @@ import ( // WS . type WS struct { - dialer proxy.Dialer addr string - client *Client } -func init() { - proxy.RegisterDialer("ws", NewWSDialer) -} - // NewWS returns a websocket proxy. func NewWS(s string, dialer proxy.Dialer) (*WS, error) { u, err := url.Parse(s) @@ -50,41 +42,9 @@ func NewWS(s string, dialer proxy.Dialer) (*WS, error) { } p := &WS{ - dialer: dialer, addr: addr, client: client, } return p, nil } - -// NewWSDialer returns a ws proxy dialer. -func NewWSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { - return NewWS(s, dialer) -} - -// Addr returns forwarder's address -func (s *WS) Addr() string { - if s.addr == "" { - return s.dialer.Addr() - } - return s.addr -} - -// NextDialer returns the next dialer -func (s *WS) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } - -// Dial connects to the address addr on the network net via the proxy. -func (s *WS) Dial(network, addr string) (net.Conn, error) { - rc, err := s.dialer.Dial("tcp", s.addr) - if err != nil { - return nil, err - } - - return s.client.NewConn(rc, addr) -} - -// DialUDP connects to the given address via the proxy. -func (s *WS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { - return nil, nil, errors.New("ws client does not support udp now") -} diff --git a/rule.go b/rule.go index 753d3dc..4ae1150 100644 --- a/rule.go +++ b/rule.go @@ -23,20 +23,7 @@ func NewRuleDialer(rules []*RuleConf, gDialer proxy.Dialer) *RuleDialer { rd := &RuleDialer{gDialer: gDialer} for _, r := range rules { - var fwdrs []proxy.Dialer - for _, chain := range r.Forward { - var fwdr proxy.Dialer - var err error - for _, url := range strings.Split(chain, ",") { - fwdr, err = proxy.DialerFromURL(url, fwdr) - if err != nil { - log.Fatal(err) - } - } - fwdrs = append(fwdrs, fwdr) - } - - sDialer := NewStrategyDialer(r.Strategy, fwdrs, r.CheckWebSite, r.CheckInterval) + sDialer := StrategyDialer(r.Forward, &r.StrategyConfig) for _, domain := range r.Domain { rd.domainMap.Store(strings.ToLower(domain), sDialer) @@ -51,7 +38,6 @@ func NewRuleDialer(rules []*RuleConf, gDialer proxy.Dialer) *RuleDialer { rd.cidrMap.Store(cidr, sDialer) } } - } return rd diff --git a/strategy.go b/strategy.go index 3324d85..b9670a1 100644 --- a/strategy.go +++ b/strategy.go @@ -12,27 +12,44 @@ import ( "github.com/nadoo/glider/proxy" ) -// NewStrategyDialer returns a new Strategy proxy.Dialer -func NewStrategyDialer(strategy string, dialers []proxy.Dialer, website string, interval int) proxy.Dialer { - if len(dialers) == 0 { +// StrategyConfig . +type StrategyConfig struct { + Strategy string + CheckWebSite string + CheckInterval int +} + +// StrategyDialer . +func StrategyDialer(s []string, c *StrategyConfig) proxy.Dialer { + // global forwarders in xx.conf + var fwdrs []*proxy.Forwarder + for _, chain := range s { + fwdr, err := proxy.ForwarderFromURL(chain) + if err != nil { + log.Fatal(err) + } + fwdrs = append(fwdrs, fwdr) + } + + if len(fwdrs) == 0 { return proxy.Direct } - if len(dialers) == 1 { - return dialers[0] + if len(fwdrs) == 1 { + return fwdrs[0] } var dialer proxy.Dialer - switch strategy { + switch c.Strategy { case "rr": - dialer = newRRDialer(dialers, website, interval) + dialer = newRRDialer(fwdrs, c.CheckWebSite, c.CheckInterval) log.F("forward to remote servers in round robin mode.") case "ha": - dialer = newHADialer(dialers, website, interval) + dialer = newHADialer(fwdrs, c.CheckWebSite, c.CheckInterval) log.F("forward to remote servers in high availability mode.") default: - log.F("not supported forward mode '%s', just use the first forward server.", conf.Strategy) - dialer = dialers[0] + log.F("not supported forward mode '%s', just use the first forward server.", c.Strategy) + dialer = fwdrs[0] } return dialer @@ -40,8 +57,8 @@ func NewStrategyDialer(strategy string, dialers []proxy.Dialer, website string, // rrDialer is the base struct of strategy dialer type rrDialer struct { - dialers []proxy.Dialer - idx int + fwdrs []*proxy.Forwarder + idx int status sync.Map @@ -51,13 +68,13 @@ type rrDialer struct { } // newRRDialer returns a new rrDialer -func newRRDialer(dialers []proxy.Dialer, website string, interval int) *rrDialer { - rr := &rrDialer{dialers: dialers} +func newRRDialer(fwdrs []*proxy.Forwarder, website string, interval int) *rrDialer { + rr := &rrDialer{fwdrs: fwdrs} rr.website = website rr.interval = interval - for k := range dialers { + for k := range fwdrs { rr.status.Store(k, true) go rr.checkDialer(k) } @@ -74,8 +91,8 @@ func (rr *rrDialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo ne return rr.NextDialer(addr).DialUDP(network, addr) } -func (rr *rrDialer) NextDialer(dstAddr string) proxy.Dialer { - n := len(rr.dialers) +func (rr *rrDialer) nextDialer(dstAddr string) *proxy.Forwarder { + n := len(rr.fwdrs) if n == 1 { rr.idx = 0 } @@ -94,7 +111,11 @@ func (rr *rrDialer) NextDialer(dstAddr string) proxy.Dialer { log.F("NO AVAILABLE PROXY FOUND! please check your network or proxy server settings.") } - return rr.dialers[rr.idx] + return rr.fwdrs[rr.idx] +} + +func (rr *rrDialer) NextDialer(dstAddr string) proxy.Dialer { + return rr.nextDialer(dstAddr) } // Check dialer @@ -106,7 +127,7 @@ func (rr *rrDialer) checkDialer(idx int) { rr.website = rr.website + ":80" } - d := rr.dialers[idx] + d := rr.fwdrs[idx] for { time.Sleep(time.Duration(rr.interval) * time.Second * time.Duration(retry>>1)) @@ -150,27 +171,27 @@ type haDialer struct { } // newHADialer . -func newHADialer(dialers []proxy.Dialer, webhost string, duration int) proxy.Dialer { +func newHADialer(dialers []*proxy.Forwarder, webhost string, duration int) proxy.Dialer { return &haDialer{rrDialer: newRRDialer(dialers, webhost, duration)} } func (ha *haDialer) Dial(network, addr string) (net.Conn, error) { - d := ha.dialers[ha.idx] + d := ha.fwdrs[ha.idx] result, ok := ha.status.Load(ha.idx) if ok && !result.(bool) { - d = ha.NextDialer(addr) + d = ha.nextDialer(addr) } return d.Dial(network, addr) } func (ha *haDialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { - d := ha.dialers[ha.idx] + d := ha.fwdrs[ha.idx] result, ok := ha.status.Load(ha.idx) if ok && !result.(bool) { - d = ha.NextDialer(addr) + d = ha.nextDialer(addr) } return d.DialUDP(network, addr)