From a46ab20901e31e4ccaadf69b2e07f0133445197b Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Sun, 12 Aug 2018 12:37:25 +0800 Subject: [PATCH] forwarder: add the ability to get parameters like 'priority' --- conf.go | 14 +- dns/client.go | 11 +- dns/server.go | 4 +- main.go | 15 +- proxy/forwarder.go | 74 +++- proxy/http/dialer.go | 90 ---- proxy/http/http.go | 225 +++++++++- proxy/http/server.go | 184 -------- proxy/mixed/{server.go => mixed.go} | 39 +- .../redir/{server_linux.go => redir_linux.go} | 39 +- ...server_linux_386.go => redir_linux_386.go} | 0 ...er_linux_other.go => redir_linux_other.go} | 0 proxy/server.go | 10 +- proxy/socks5/dialer.go | 251 ----------- proxy/socks5/server.go | 214 --------- proxy/socks5/socks5.go | 417 +++++++++++++++++- proxy/ss/dialer.go | 86 ---- proxy/ss/server.go | 194 -------- proxy/ss/ss.go | 236 +++++++++- proxy/ssr/dialer.go | 130 ------ proxy/ssr/ssr.go | 113 ++++- proxy/tcptun/server.go | 80 ---- proxy/tcptun/tcptun.go | 69 ++- proxy/tls/dialer.go | 65 --- proxy/tls/tls.go | 48 +- .../{server_linux.go => tproxy_linux.go} | 36 +- proxy/udptun/server.go | 92 ---- proxy/udptun/udptun.go | 81 +++- proxy/uottun/server.go | 90 ---- proxy/uottun/uottun.go | 79 +++- proxy/vmess/dialer.go | 60 --- proxy/vmess/vmess.go | 44 +- proxy/ws/dialer.go | 60 --- proxy/ws/ws.go | 40 ++ 34 files changed, 1480 insertions(+), 1710 deletions(-) delete mode 100644 proxy/http/dialer.go delete mode 100644 proxy/http/server.go rename proxy/mixed/{server.go => mixed.go} (77%) rename proxy/redir/{server_linux.go => redir_linux.go} (86%) rename proxy/redir/{server_linux_386.go => redir_linux_386.go} (100%) rename proxy/redir/{server_linux_other.go => redir_linux_other.go} (100%) delete mode 100644 proxy/socks5/dialer.go delete mode 100644 proxy/socks5/server.go delete mode 100644 proxy/ss/dialer.go delete mode 100644 proxy/ss/server.go delete mode 100644 proxy/ssr/dialer.go delete mode 100644 proxy/tcptun/server.go delete mode 100644 proxy/tls/dialer.go rename proxy/tproxy/{server_linux.go => tproxy_linux.go} (88%) delete mode 100644 proxy/udptun/server.go delete mode 100644 proxy/uottun/server.go delete mode 100644 proxy/vmess/dialer.go delete mode 100644 proxy/ws/dialer.go diff --git a/conf.go b/conf.go index 13abcc5..7cb8ce6 100644 --- a/conf.go +++ b/conf.go @@ -27,11 +27,11 @@ var conf struct { RulesDir string DNS string - DNSServer []string + DNSServers []string DNSTimeout int DNSMaxTTL int DNSMinTTL int - DNSRecord []string + DNSRecords []string IPSet string @@ -51,11 +51,11 @@ func confInit() { flag.StringVar(&conf.RulesDir, "rules-dir", "", "rule file folder") flag.StringVar(&conf.DNS, "dns", "", "dns forwarder server listen address") - flag.StringSliceUniqVar(&conf.DNSServer, "dnsserver", []string{"8.8.8.8:53"}, "remote dns server") + flag.StringSliceUniqVar(&conf.DNSServers, "dnsserver", []string{"8.8.8.8:53"}, "remote dns server") flag.IntVar(&conf.DNSTimeout, "dnstimeout", 3, "timeout value used in multiple dnsservers switch(seconds)") flag.IntVar(&conf.DNSMaxTTL, "dnsmaxttl", 1800, "maximum TTL value for entries in the CACHE(seconds)") flag.IntVar(&conf.DNSMinTTL, "dnsminttl", 0, "minimum TTL value for entries in the CACHE(seconds)") - flag.StringSliceUniqVar(&conf.DNSRecord, "dnsrecord", nil, "custom dns record, format: domain/ip") + flag.StringSliceUniqVar(&conf.DNSRecords, "dnsrecord", nil, "custom dns record, format: domain/ip") flag.StringVar(&conf.IPSet, "ipset", "", "ipset name") @@ -127,8 +127,8 @@ type RuleConf struct { Forward []string StrategyConfig strategy.Config - DNSServer []string - IPSet string + DNSServers []string + IPSet string Domain []string IP []string @@ -145,7 +145,7 @@ func NewRuleConfFromFile(ruleFile string) (*RuleConf, error) { 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.StringSliceUniqVar(&p.DNSServers, "dnsserver", nil, "remote dns server") f.StringVar(&p.IPSet, "ipset", "", "ipset name") f.StringSliceUniqVar(&p.Domain, "domain", nil, "domain") diff --git a/dns/client.go b/dns/client.go index 247ef38..f5ef83d 100644 --- a/dns/client.go +++ b/dns/client.go @@ -18,9 +18,11 @@ type HandleFunc func(Domain, ip string) error // Config for dns type Config struct { + Servers []string Timeout int MaxTTL int MinTTL int + Records []string } // Client is a dns client struct @@ -34,15 +36,20 @@ type Client struct { } // NewClient returns a new dns client -func NewClient(dialer proxy.Dialer, upServers []string, config *Config) (*Client, error) { +func NewClient(dialer proxy.Dialer, config *Config) (*Client, error) { c := &Client{ dialer: dialer, cache: NewCache(), config: config, - upServers: upServers, + upServers: config.Servers, upServerMap: make(map[string][]string), } + // custom records + for _, record := range config.Records { + c.AddRecord(record) + } + return c, nil } diff --git a/dns/server.go b/dns/server.go index bbd8bc4..13243d7 100644 --- a/dns/server.go +++ b/dns/server.go @@ -21,8 +21,8 @@ type Server struct { } // NewServer returns a new dns server -func NewServer(addr string, dialer proxy.Dialer, upServers []string, config *Config) (*Server, error) { - c, err := NewClient(dialer, upServers, config) +func NewServer(addr string, dialer proxy.Dialer, config *Config) (*Server, error) { + c, err := NewClient(dialer, config) s := &Server{ addr: addr, Client: c, diff --git a/main.go b/main.go index 528ec37..09d6f72 100644 --- a/main.go +++ b/main.go @@ -41,25 +41,22 @@ func main() { // DNS Server if conf.DNS != "" { dnscfg := &dns.Config{ + Servers: conf.DNSServers, Timeout: conf.DNSTimeout, MaxTTL: conf.DNSMaxTTL, - MinTTL: conf.DNSMinTTL} + MinTTL: conf.DNSMinTTL, + Records: conf.DNSRecords} - d, err := dns.NewServer(conf.DNS, dialer, conf.DNSServer, dnscfg) + d, err := dns.NewServer(conf.DNS, dialer, dnscfg) if err != nil { log.Fatal(err) } - // custom records - for _, record := range conf.DNSRecord { - d.AddRecord(record) - } - // rule for _, r := range conf.rules { for _, domain := range r.Domain { - if len(r.DNSServer) > 0 { - d.SetServer(domain, r.DNSServer...) + if len(r.DNSServers) > 0 { + d.SetServer(domain, r.DNSServers...) } } } diff --git a/proxy/forwarder.go b/proxy/forwarder.go index 0f640b7..d88aa5a 100644 --- a/proxy/forwarder.go +++ b/proxy/forwarder.go @@ -1,32 +1,40 @@ package proxy import ( + "net" + "net/url" + "strconv" "strings" + "sync/atomic" ) // Forwarder is a forwarder type Forwarder struct { Dialer + Priority int addr string - disabled bool - failures int - priority int - weight int + disabled uint32 + failures uint32 latency int } // ForwarderFromURL returns a new forwarder -func ForwarderFromURL(s string) (*Forwarder, error) { +func ForwarderFromURL(s string) (f *Forwarder, err error) { + ss := strings.Split(s, "#") var d Dialer - var err error - for _, url := range strings.Split(s, ",") { + for _, url := range strings.Split(ss[0], ",") { d, err = DialerFromURL(url, d) if err != nil { return nil, err } } - return &Forwarder{Dialer: d}, nil + f = NewForwarder(d) + if len(ss) > 1 { + err = f.parseOption(ss[1]) + } + + return f, err } // NewForwarder . @@ -34,17 +42,61 @@ func NewForwarder(dialer Dialer) *Forwarder { return &Forwarder{Dialer: dialer, addr: dialer.Addr()} } +func (f *Forwarder) parseOption(option string) error { + query, err := url.ParseQuery(option) + if err != nil { + return err + } + + var priority uint64 + p := query.Get("priority") + if p != "" { + priority, err = strconv.ParseUint(p, 10, 32) + } + f.Priority = int(priority) + + return err +} + // Addr . func (f *Forwarder) Addr() string { return f.addr } +// Dial . +func (f *Forwarder) Dial(network, addr string) (c net.Conn, err error) { + c, err = f.Dialer.Dial(network, addr) + + // TODO: proxy timeout, target timeout? + if err != nil { + atomic.AddUint32(&f.failures, 1) + // log.F("forward dial failed, %d", f.failures) + } + + return c, err +} + +// Failures returns the failuer count of forwarder +func (f *Forwarder) Failures() uint32 { + return atomic.LoadUint32(&f.failures) +} + // Enable . -func (f *Forwarder) Enable(b bool) { - f.disabled = !b +func (f *Forwarder) Enable() { + atomic.StoreUint32(&f.failures, 0) + atomic.StoreUint32(&f.failures, 0) +} + +// Disable . +func (f *Forwarder) Disable() { + atomic.StoreUint32(&f.failures, 1) } // Enabled . func (f *Forwarder) Enabled() bool { - return !f.disabled + return !isTrue(atomic.LoadUint32(&f.disabled)) +} + +func isTrue(n uint32) bool { + return n&1 == 1 } diff --git a/proxy/http/dialer.go b/proxy/http/dialer.go deleted file mode 100644 index e522b93..0000000 --- a/proxy/http/dialer.go +++ /dev/null @@ -1,90 +0,0 @@ -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 4aea435..49615e3 100644 --- a/proxy/http/http.go +++ b/proxy/http/http.go @@ -4,23 +4,38 @@ 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 } -// NewHTTP returns a http base struct -func NewHTTP(s string) (*HTTP, error) { +func init() { + proxy.RegisterDialer("http", NewHTTPDialer) + proxy.RegisterServer("http", NewHTTPServer) +} + +// NewHTTP returns a http proxy. +func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -32,6 +47,7 @@ func NewHTTP(s string) (*HTTP, error) { pass, _ := u.User.Password() h := &HTTP{ + dialer: dialer, addr: addr, user: user, password: pass, @@ -40,12 +56,215 @@ func NewHTTP(s string) (*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 deleted file mode 100644 index e59b4fe..0000000 --- a/proxy/http/server.go +++ /dev/null @@ -1,184 +0,0 @@ -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/server.go b/proxy/mixed/mixed.go similarity index 77% rename from proxy/mixed/server.go rename to proxy/mixed/mixed.go index 9aa58f1..5780d8b 100644 --- a/proxy/mixed/server.go +++ b/proxy/mixed/mixed.go @@ -12,10 +12,6 @@ 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"), @@ -28,40 +24,45 @@ var httpMethods = [...][]byte{ []byte("TRACE"), } -// Server struct -type Server struct { - *proxy.Forwarder +// MixedProxy struct +type MixedProxy struct { + dialer proxy.Dialer addr string - http *http.Server - socks5 *socks5.Server + + http *http.HTTP + socks5 *socks5.SOCKS5 +} + +func init() { + proxy.RegisterServer("mixed", NewMixedProxyServer) } // NewMixedProxy returns a mixed proxy. -func NewMixedProxy(s string, f *proxy.Forwarder) (*Server, error) { +func NewMixedProxy(s string, dialer proxy.Dialer) (*MixedProxy, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) return nil, err } - p := &Server{ - Forwarder: f, - addr: u.Host, + p := &MixedProxy{ + dialer: dialer, + addr: u.Host, } - p.http, _ = http.NewServer(s, f) - p.socks5, _ = socks5.NewServer(s, f) + p.http, _ = http.NewHTTP(s, dialer) + p.socks5, _ = socks5.NewSOCKS5(s, dialer) return p, nil } // NewMixedProxyServer returns a mixed proxy server. -func NewMixedProxyServer(s string, f *proxy.Forwarder) (proxy.Server, error) { - return NewMixedProxy(s, f) +func NewMixedProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) { + return NewMixedProxy(s, dialer) } // ListenAndServe . -func (p *Server) ListenAndServe() { +func (p *MixedProxy) ListenAndServe() { go p.socks5.ListenAndServeUDP() @@ -85,7 +86,7 @@ func (p *Server) ListenAndServe() { } // Serve . -func (p *Server) Serve(c net.Conn) { +func (p *MixedProxy) Serve(c net.Conn) { defer c.Close() if c, ok := c.(*net.TCPConn); ok { diff --git a/proxy/redir/server_linux.go b/proxy/redir/redir_linux.go similarity index 86% rename from proxy/redir/server_linux.go rename to proxy/redir/redir_linux.go index 590a33f..1d202fc 100644 --- a/proxy/redir/server_linux.go +++ b/proxy/redir/redir_linux.go @@ -16,10 +16,6 @@ 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 @@ -27,31 +23,40 @@ const ( IP6T_SO_ORIGINAL_DST = 80 ) -// Server struct -type Server struct { - addr string - *proxy.Forwarder +// RedirProxy struct +type RedirProxy struct { + dialer proxy.Dialer + addr string } -// NewServer returns a local proxy server -func NewServer(s string, f *proxy.Forwarder) (*Server, error) { +func init() { + proxy.RegisterServer("redir", NewRedirServer) +} + +// NewRedirProxy returns a redirect proxy. +func NewRedirProxy(s string, dialer proxy.Dialer) (*RedirProxy, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) return nil, err } - server := &Server{addr: u.Host, Forwarder: f} - return server, nil + addr := u.Host + r := &RedirProxy{ + dialer: dialer, + addr: addr, + } + + return r, nil } -// CreateServer returns a local proxy server -func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) { - return NewServer(s, f) +// NewRedirServer returns a redir server. +func NewRedirServer(s string, dialer proxy.Dialer) (proxy.Server, error) { + return NewRedirProxy(s, dialer) } // ListenAndServe . -func (s *Server) ListenAndServe() { +func (s *RedirProxy) ListenAndServe() { l, err := net.Listen("tcp", s.addr) if err != nil { log.F("[redir] failed to listen on %s: %v", s.addr, err) @@ -80,7 +85,7 @@ func (s *Server) ListenAndServe() { return } - rc, err := s.Dial("tcp", tgt.String()) + rc, err := s.dialer.Dial("tcp", tgt.String()) if err != nil { log.F("[redir] failed to connect to target: %v", err) return diff --git a/proxy/redir/server_linux_386.go b/proxy/redir/redir_linux_386.go similarity index 100% rename from proxy/redir/server_linux_386.go rename to proxy/redir/redir_linux_386.go diff --git a/proxy/redir/server_linux_other.go b/proxy/redir/redir_linux_other.go similarity index 100% rename from proxy/redir/server_linux_other.go rename to proxy/redir/redir_linux_other.go diff --git a/proxy/server.go b/proxy/server.go index 3217722..4bc4e1d 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, f *Forwarder) (Server, error) +type ServerCreator func(s string, dialer Dialer) (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, f *Forwarder) (Server, error) { +func ServerFromURL(s string, dialer Dialer) (Server, error) { if !strings.Contains(s, "://") { s = "mixed://" + s } @@ -38,13 +38,13 @@ func ServerFromURL(s string, f *Forwarder) (Server, error) { return nil, err } - if f == nil { - f = NewForwarder(Direct) + if dialer == nil { + dialer = Direct } c, ok := serverMap[strings.ToLower(u.Scheme)] if ok { - return c(s, f) + return c(s, dialer) } return nil, errors.New("unknown scheme '" + u.Scheme + "'") diff --git a/proxy/socks5/dialer.go b/proxy/socks5/dialer.go deleted file mode 100644 index 383cc6a..0000000 --- a/proxy/socks5/dialer.go +++ /dev/null @@ -1,251 +0,0 @@ -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 deleted file mode 100644 index a1eb984..0000000 --- a/proxy/socks5/server.go +++ /dev/null @@ -1,214 +0,0 @@ -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 d9d01b2..3d0cf51 100644 --- a/proxy/socks5/socks5.go +++ b/proxy/socks5/socks5.go @@ -12,9 +12,18 @@ 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 @@ -22,14 +31,20 @@ 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) (*SOCKS5, error) { +func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -41,6 +56,7 @@ func NewSOCKS5(s string) (*SOCKS5, error) { pass, _ := u.User.Password() h := &SOCKS5{ + dialer: dialer, addr: addr, user: user, password: pass, @@ -48,3 +64,402 @@ func NewSOCKS5(s string) (*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 deleted file mode 100644 index a9e89bd..0000000 --- a/proxy/ss/dialer.go +++ /dev/null @@ -1,86 +0,0 @@ -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 deleted file mode 100644 index e593ca6..0000000 --- a/proxy/ss/server.go +++ /dev/null @@ -1,194 +0,0 @@ -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 b8b2b63..919ac95 100644 --- a/proxy/ss/ss.go +++ b/proxy/ss/ss.go @@ -1,22 +1,36 @@ 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 { - addr string + dialer proxy.Dialer + addr string + core.Cipher } +func init() { + proxy.RegisterDialer("ss", NewSSDialer) + proxy.RegisterServer("ss", NewSSServer) +} + // NewSS returns a shadowsocks proxy. -func NewSS(s string) (*SS, error) { +func NewSS(s string, dialer proxy.Dialer) (*SS, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -33,6 +47,7 @@ func NewSS(s string) (*SS, error) { } p := &SS{ + dialer: dialer, addr: addr, Cipher: ciph, } @@ -40,7 +55,224 @@ func NewSS(s string) (*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 deleted file mode 100644 index fbe29e0..0000000 --- a/proxy/ssr/dialer.go +++ /dev/null @@ -1,130 +0,0 @@ -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 c392174..216cae0 100644 --- a/proxy/ssr/ssr.go +++ b/proxy/ssr/ssr.go @@ -1,14 +1,26 @@ 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 { - addr string + dialer proxy.Dialer + addr string EncryptMethod string EncryptPassword string @@ -20,8 +32,12 @@ 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) (*SSR, error) { +func NewSSR(s string, dialer proxy.Dialer) (*SSR, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -33,6 +49,7 @@ func NewSSR(s string) (*SSR, error) { pass, _ := u.User.Password() p := &SSR{ + dialer: dialer, addr: addr, EncryptMethod: method, EncryptPassword: pass, @@ -46,3 +63,95 @@ func NewSSR(s string) (*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 deleted file mode 100644 index 6964427..0000000 --- a/proxy/tcptun/server.go +++ /dev/null @@ -1,80 +0,0 @@ -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 abbc409..2f2a6c7 100644 --- a/proxy/tcptun/tcptun.go +++ b/proxy/tcptun/tcptun.go @@ -1,20 +1,29 @@ 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 { - addr string + dialer proxy.Dialer + addr string + raddr string } +func init() { + proxy.RegisterServer("tcptun", NewTCPTunServer) +} + // NewTCPTun returns a tcptun proxy. -func NewTCPTun(s string) (*TCPTun, error) { +func NewTCPTun(s string, dialer proxy.Dialer) (*TCPTun, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -25,9 +34,61 @@ func NewTCPTun(s string) (*TCPTun, error) { d := strings.Split(addr, "=") p := &TCPTun{ - addr: d[0], - raddr: d[1], + dialer: dialer, + 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 deleted file mode 100644 index fbec203..0000000 --- a/proxy/tls/dialer.go +++ /dev/null @@ -1,65 +0,0 @@ -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 9dda7f5..d4f8cab 100644 --- a/proxy/tls/tls.go +++ b/proxy/tls/tls.go @@ -1,22 +1,31 @@ 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 { - addr string + dialer proxy.Dialer + addr string serverName string skipVerify bool } +func init() { + proxy.RegisterDialer("tls", NewTLSDialer) +} + // NewTLS returns a tls proxy. -func NewTLS(s string) (*TLS, error) { +func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) { u, err := url.Parse(s) if err != nil { log.F("parse url err: %s", err) @@ -35,6 +44,7 @@ func NewTLS(s string) (*TLS, error) { serverName := addr[:colonPos] p := &TLS{ + dialer: dialer, addr: addr, serverName: serverName, skipVerify: false, @@ -46,3 +56,37 @@ func NewTLS(s string) (*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/server_linux.go b/proxy/tproxy/tproxy_linux.go similarity index 88% rename from proxy/tproxy/server_linux.go rename to proxy/tproxy/tproxy_linux.go index 14b9265..6398eb4 100644 --- a/proxy/tproxy/server_linux.go +++ b/proxy/tproxy/tproxy_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", CreateServer) + proxy.RegisterServer("tproxy", NewTProxyServer) } -// Server struct -type Server struct { - addr string - *proxy.Forwarder -} - -// NewServer returns a local proxy server -func NewServer(s string, f *proxy.Forwarder) (*Server, error) { +// NewTProxy returns a tproxy. +func NewTProxy(s string, dialer proxy.Dialer) (*TProxy, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -37,32 +37,32 @@ func NewServer(s string, f *proxy.Forwarder) (*Server, error) { addr := u.Host - p := &Server{ - addr: addr, - Forwarder: f, + p := &TProxy{ + dialer: dialer, + addr: addr, } return p, nil } -// CreateServer returns a local proxy server -func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) { - return NewServer(s, f) +// NewTProxyServer returns a udp tunnel server. +func NewTProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) { + return NewTProxy(s, dialer) } // ListenAndServe . -func (s *Server) ListenAndServe() { +func (s *TProxy) ListenAndServe() { // go s.ListenAndServeTCP() s.ListenAndServeUDP() } // ListenAndServeTCP . -func (s *Server) ListenAndServeTCP() { +func (s *TProxy) ListenAndServeTCP() { log.F("[tproxy] tcp mode not supported now, please use 'redir' instead") } // ListenAndServeUDP . -func (s *Server) ListenAndServeUDP() { +func (s *TProxy) 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 deleted file mode 100644 index 5819c9d..0000000 --- a/proxy/udptun/server.go +++ /dev/null @@ -1,92 +0,0 @@ -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 60afe10..8fb72ff 100644 --- a/proxy/udptun/udptun.go +++ b/proxy/udptun/udptun.go @@ -1,20 +1,31 @@ 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 { - addr string + dialer proxy.Dialer + addr string + raddr string } +func init() { + proxy.RegisterServer("udptun", NewUDPTunServer) +} + // NewUDPTun returns a UDPTun proxy. -func NewUDPTun(s string) (*UDPTun, error) { +func NewUDPTun(s string, dialer proxy.Dialer) (*UDPTun, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -25,9 +36,71 @@ func NewUDPTun(s string) (*UDPTun, error) { d := strings.Split(addr, "=") p := &UDPTun{ - addr: d[0], - raddr: d[1], + dialer: dialer, + 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 deleted file mode 100644 index eb0bddf..0000000 --- a/proxy/uottun/server.go +++ /dev/null @@ -1,90 +0,0 @@ -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 83142ba..f43418c 100644 --- a/proxy/uottun/uottun.go +++ b/proxy/uottun/uottun.go @@ -1,20 +1,31 @@ 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 { - addr string + dialer proxy.Dialer + addr string + raddr string } +func init() { + proxy.RegisterServer("uottun", NewUoTTunServer) +} + // NewUoTTun returns a UoTTun proxy. -func NewUoTTun(s string) (*UoTTun, error) { +func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) { u, err := url.Parse(s) if err != nil { log.F("parse err: %s", err) @@ -25,9 +36,69 @@ func NewUoTTun(s string) (*UoTTun, error) { d := strings.Split(addr, "=") p := &UoTTun{ - addr: d[0], - raddr: d[1], + dialer: dialer, + 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 deleted file mode 100644 index 5f875c9..0000000 --- a/proxy/vmess/dialer.go +++ /dev/null @@ -1,60 +0,0 @@ -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 ccc1068..6e46481 100644 --- a/proxy/vmess/vmess.go +++ b/proxy/vmess/vmess.go @@ -1,15 +1,19 @@ package vmess import ( + "errors" + "net" "net/url" "strconv" "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" ) // VMess . type VMess struct { - addr string + dialer proxy.Dialer + addr string uuid string alterID int @@ -18,8 +22,12 @@ type VMess struct { client *Client } +func init() { + proxy.RegisterDialer("vmess", NewVMessDialer) +} + // NewVMess returns a vmess proxy. -func NewVMess(s string) (*VMess, error) { +func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) { u, err := url.Parse(s) if err != nil { log.F("parse url err: %s", err) @@ -54,6 +62,7 @@ func NewVMess(s string) (*VMess, error) { } p := &VMess{ + dialer: dialer, addr: addr, uuid: uuid, alterID: int(alterID), @@ -63,3 +72,34 @@ func NewVMess(s string) (*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 deleted file mode 100644 index ec39b8f..0000000 --- a/proxy/ws/dialer.go +++ /dev/null @@ -1,60 +0,0 @@ -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 f07cd5d..3f6bf48 100644 --- a/proxy/ws/ws.go +++ b/proxy/ws/ws.go @@ -1,6 +1,8 @@ package ws import ( + "errors" + "net" "net/url" "strings" @@ -10,10 +12,16 @@ 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) @@ -42,9 +50,41 @@ 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") +}