From cd42337169e3a628624cc6a8aefaceb37b90a9b9 Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Sat, 3 Oct 2020 22:42:34 +0800 Subject: [PATCH] chore: update doc for info and restructure codes --- README.md | 13 +- config.go | 8 +- config/glider.conf.example | 3 + main.go | 2 +- proxy/socks5/client.go | 230 ++++++++++++++++++ proxy/socks5/server.go | 258 ++++++++++++++++++++ proxy/socks5/socks5.go | 469 ------------------------------------- proxy/ss/client.go | 62 +++++ proxy/ss/server.go | 178 ++++++++++++++ proxy/ss/ss.go | 223 ------------------ proxy/vless/server.go | 16 +- 11 files changed, 761 insertions(+), 701 deletions(-) create mode 100644 proxy/socks5/client.go create mode 100644 proxy/socks5/server.go create mode 100644 proxy/ss/client.go create mode 100644 proxy/ss/server.go diff --git a/README.md b/README.md index 9463046..0695706 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![GitHub release](https://img.shields.io/github/v/release/nadoo/glider.svg?style=flat-square&include_prereleases)](https://github.com/nadoo/glider/releases) [![Actions Status](https://img.shields.io/github/workflow/status/nadoo/glider/Build?style=flat-square)](https://github.com/nadoo/glider/actions) -glider is a forward proxy with multiple protocols support, and also a dns/dhcp forwarding server with ipset management features(like dnsmasq). +glider is a forward proxy with multiple protocols support, and also a dns/dhcp server with ipset management features(like dnsmasq). we can set up local listeners as proxy servers, and forward requests to internet via forwarders. @@ -53,8 +53,8 @@ we can set up local listeners as proxy servers, and forward requests to internet |ssr | | |√| |client only |ssh | | |√| |client only |trojan | | |√|√|client only +|vless |√|√|√|√|client only |vmess | | |√| |client only -|vless | | |√|√|client only |redir |√| | | |linux only |redir6 |√| | | |linux only(ipv6) |tls |√| |√| |transport client & server @@ -88,7 +88,7 @@ glider -h click to see details ```bash -./glider 0.11.0 usage: +./glider 0.11.1 usage: -checkdisabledonly check disabled fowarders only -checkinterval int @@ -139,8 +139,11 @@ glider -h verbose mode Available schemes: - listen: mixed ss socks5 http redir redir6 tcptun udptun uottun tls unix kcp - forward: reject ss socks4 socks5 http ssr ssh vmess vless trojan tls ws unix kcp simple-obfs + listen: mixed ss socks5 http vless redir redir6 tcptun udptun uottun tls unix kcp + forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan tls ws unix kcp simple-obfs + +Socks5 scheme: + socks://[user:pass@]host:port SS scheme: ss://method:pass@host:port diff --git a/config.go b/config.go index 5b4233a..cefbede 100644 --- a/config.go +++ b/config.go @@ -127,8 +127,12 @@ func usage() { fmt.Fprintf(w, "\n") fmt.Fprintf(w, "Available schemes:\n") - fmt.Fprintf(w, " listen: mixed ss socks5 http redir redir6 tcptun udptun uottun tls unix kcp\n") - fmt.Fprintf(w, " forward: reject ss socks4 socks5 http ssr ssh vmess vless trojan tls ws unix kcp simple-obfs\n") + fmt.Fprintf(w, " listen: mixed ss socks5 http vless redir redir6 tcptun udptun uottun tls unix kcp\n") + fmt.Fprintf(w, " forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan tls ws unix kcp simple-obfs\n") + fmt.Fprintf(w, "\n") + + fmt.Fprintf(w, "Socks5 scheme:\n") + fmt.Fprintf(w, " socks://[user:pass@]host:port\n") fmt.Fprintf(w, "\n") fmt.Fprintf(w, "SS scheme:\n") diff --git a/config/glider.conf.example b/config/glider.conf.example index fff5acc..fb309e0 100644 --- a/config/glider.conf.example +++ b/config/glider.conf.example @@ -43,6 +43,9 @@ listen=http://:8080 # listen on 1080 as a socks5 proxy server. listen=socks5://:1080 +# listen on 1234 as vless proxy server. +# listen=vless://uuid@:1234 + # listen on 1081 as a linux transparent proxy server. # listen=redir://:1081 diff --git a/main.go b/main.go index 7045a6c..4ebd7e5 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,7 @@ import ( ) var ( - version = "0.11.0" + version = "0.11.1" config = parseConfig() ) diff --git a/proxy/socks5/client.go b/proxy/socks5/client.go new file mode 100644 index 0000000..e79a07d --- /dev/null +++ b/proxy/socks5/client.go @@ -0,0 +1,230 @@ +package socks5 + +import ( + "errors" + "io" + "net" + "strconv" + + "github.com/nadoo/glider/log" + "github.com/nadoo/glider/pool" + "github.com/nadoo/glider/proxy" + "github.com/nadoo/glider/proxy/socks" +) + +// NewSocks5Dialer returns a socks5 proxy dialer. +func NewSocks5Dialer(s string, d proxy.Dialer) (proxy.Dialer, error) { + return NewSocks5(s, d, nil) +} + +// Addr returns forwarder's address. +func (s *Socks5) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} + +// 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("[socks5]: 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{Version, 1, 0}) + + buf := pool.GetBuffer(socks.MaxAddrLen) + defer pool.PutBuffer(buf) + + // 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{Version, 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] != Version { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) + } + if buf[1] == 0xff { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") + } + + if buf[1] == socks.AuthPassword { + buf = buf[:0] + buf = append(buf, 1 /* password protocol version */) + buf = append(buf, uint8(len(s.user))) + buf = append(buf, s.user...) + buf = append(buf, uint8(len(s.password))) + buf = append(buf, s.password...) + + if _, err := conn.Write(buf); err != nil { + return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if buf[1] != 0 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") + } + } + + buf = buf[:0] + buf = append(buf, Version, socks.CmdConnect, 0 /* reserved */) + + if ip := net.ParseIP(host); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + buf = append(buf, socks.ATypIP4) + ip = ip4 + } else { + buf = append(buf, socks.ATypIP6) + } + buf = append(buf, ip...) + } else { + if len(host) > 255 { + return errors.New("proxy: destination hostname too long: " + host) + } + buf = append(buf, socks.ATypDomain) + buf = append(buf, byte(len(host))) + buf = append(buf, host...) + } + buf = append(buf, byte(port>>8), byte(port)) + + if _, err := conn.Write(buf); err != nil { + return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:4]); err != nil { + return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + failure := "unknown error" + if int(buf[1]) < len(socks.Errors) { + failure = socks.Errors[buf[1]].Error() + } + + if len(failure) > 0 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) + } + + bytesToDiscard := 0 + switch buf[3] { + case socks.ATypIP4: + bytesToDiscard = net.IPv4len + case socks.ATypIP6: + bytesToDiscard = net.IPv6len + case socks.ATypDomain: + _, err := io.ReadFull(conn, buf[:1]) + if err != nil { + return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + bytesToDiscard = int(buf[0]) + default: + return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) + } + + if cap(buf) < bytesToDiscard { + buf = make([]byte, bytesToDiscard) + } else { + buf = buf[:bytesToDiscard] + } + if _, err := io.ReadFull(conn, buf); err != nil { + return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + // Also need to discard the port number + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + return nil +} diff --git a/proxy/socks5/server.go b/proxy/socks5/server.go new file mode 100644 index 0000000..b467653 --- /dev/null +++ b/proxy/socks5/server.go @@ -0,0 +1,258 @@ +package socks5 + +import ( + "errors" + "io" + "net" + "strings" + "sync" + "time" + + "github.com/nadoo/glider/log" + "github.com/nadoo/glider/pool" + "github.com/nadoo/glider/proxy" + "github.com/nadoo/glider/proxy/socks" +) + +// NewSocks5Server returns a socks5 proxy server. +func NewSocks5Server(s string, p proxy.Proxy) (proxy.Server, error) { + return NewSocks5(s, nil, p) +} + +// ListenAndServe serves socks5 requests. +func (s *Socks5) ListenAndServe() { + go s.ListenAndServeUDP() + s.ListenAndServeTCP() +} + +// ListenAndServeTCP listen and serve on tcp port. +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.Serve(c) + } +} + +// Serve serves a connection. +func (s *Socks5) Serve(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 := pool.GetBuffer(1) + defer pool.PutBuffer(buf) + // 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 in handshake with %s: %v", c.RemoteAddr(), err) + return + } + + rc, dialer, err := s.proxy.Dial("tcp", tgt.String()) + if err != nil { + log.F("[socks5] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + return + } + defer rc.Close() + + log.F("[socks5] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr()) + + if err = proxy.Relay(c, rc); err != nil { + log.F("[socks5] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + // record remote conn failure only + if !strings.Contains(err.Error(), s.addr) { + s.proxy.Record(dialer, false) + } + } +} + +// ListenAndServeUDP serves udp requests. +func (s *Socks5) ListenAndServeUDP() { + lc, err := net.ListenPacket("udp", s.addr) + if err != nil { + log.F("[socks5] failed to listen on UDP %s: %v", s.addr, err) + return + } + defer lc.Close() + + log.F("[socks5] listening UDP on %s", s.addr) + + var nm sync.Map + buf := make([]byte, proxy.UDPBufSize) + + for { + c := NewPktConn(lc, nil, nil, true, nil) + + n, raddr, err := c.ReadFrom(buf) + if err != nil { + log.F("[socks5u] 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("[socks5u] can not get target address, not a valid request") + continue + } + + lpc, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String()) + if err != nil { + log.F("[socks5u] remote dial error: %v", err) + continue + } + + pc = NewPktConn(lpc, nextHop, nil, false, nil) + nm.Store(raddr.String(), pc) + + go func() { + proxy.RelayUDP(c, raddr, pc, 2*time.Minute) + pc.Close() + nm.Delete(raddr.String()) + }() + + log.F("[socks5u] %s <-> %s", raddr, c.tgtAddr) + + } else { + pc = v.(*PktConn) + } + + _, err = pc.WriteTo(buf[:n], pc.writeAddr) + if err != nil { + log.F("[socks5u] remote write error: %v", err) + continue + } + + // log.F("[socks5u] %s <-> %s", raddr, c.tgtAddr) + } + +} + +// 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 s.user != "" && s.password != "" { + _, err := rw.Write([]byte{Version, socks.AuthPassword}) + if err != nil { + return nil, err + } + + _, err = io.ReadFull(rw, buf[:2]) + if err != nil { + return nil, err + } + + // Get username + userLen := int(buf[1]) + if userLen <= 0 { + rw.Write([]byte{1, 1}) + return nil, errors.New("auth failed: wrong username length") + } + + if _, err := io.ReadFull(rw, buf[:userLen]); err != nil { + return nil, errors.New("auth failed: cannot get username") + } + user := string(buf[:userLen]) + + // Get password + _, err = rw.Read(buf[:1]) + if err != nil { + return nil, errors.New("auth failed: cannot get password len") + } + + passLen := int(buf[0]) + if passLen <= 0 { + rw.Write([]byte{1, 1}) + return nil, errors.New("auth failed: wrong password length") + } + + _, err = io.ReadFull(rw, buf[:passLen]) + if err != nil { + return nil, errors.New("auth failed: cannot get password") + } + pass := string(buf[:passLen]) + + // Verify + if user != s.user || pass != s.password { + _, err = rw.Write([]byte{1, 1}) + if err != nil { + return nil, err + } + return nil, errors.New("auth failed, authinfo: " + user + ":" + pass) + } + + // Response auth state + _, err = rw.Write([]byte{1, 0}) + if err != nil { + return nil, err + } + + } else if _, err := rw.Write([]byte{Version, socks.AuthNone}); 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 1072f96..2400672 100644 --- a/proxy/socks5/socks5.go +++ b/proxy/socks5/socks5.go @@ -10,19 +10,10 @@ package socks5 import ( - "errors" - "io" - "net" "net/url" - "strconv" - "strings" - "sync" - "time" "github.com/nadoo/glider/log" - "github.com/nadoo/glider/pool" "github.com/nadoo/glider/proxy" - "github.com/nadoo/glider/proxy/socks" ) // Version is socks5 version number. @@ -65,463 +56,3 @@ func NewSocks5(s string, d proxy.Dialer, p proxy.Proxy) (*Socks5, error) { return h, nil } - -// NewSocks5Dialer returns a socks5 proxy dialer. -func NewSocks5Dialer(s string, d proxy.Dialer) (proxy.Dialer, error) { - return NewSocks5(s, d, nil) -} - -// NewSocks5Server returns a socks5 proxy server. -func NewSocks5Server(s string, p proxy.Proxy) (proxy.Server, error) { - return NewSocks5(s, nil, p) -} - -// ListenAndServe serves socks5 requests. -func (s *Socks5) ListenAndServe() { - go s.ListenAndServeUDP() - s.ListenAndServeTCP() -} - -// ListenAndServeTCP listen and serve on tcp port. -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.Serve(c) - } -} - -// Serve serves a connection. -func (s *Socks5) Serve(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 := pool.GetBuffer(1) - defer pool.PutBuffer(buf) - // 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 in handshake with %s: %v", c.RemoteAddr(), err) - return - } - - rc, dialer, err := s.proxy.Dial("tcp", tgt.String()) - if err != nil { - log.F("[socks5] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) - return - } - defer rc.Close() - - log.F("[socks5] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr()) - - if err = proxy.Relay(c, rc); err != nil { - log.F("[socks5] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) - // record remote conn failure only - if !strings.Contains(err.Error(), s.addr) { - s.proxy.Record(dialer, false) - } - } -} - -// ListenAndServeUDP serves udp requests. -func (s *Socks5) ListenAndServeUDP() { - lc, err := net.ListenPacket("udp", s.addr) - if err != nil { - log.F("[socks5] failed to listen on UDP %s: %v", s.addr, err) - return - } - defer lc.Close() - - log.F("[socks5] listening UDP on %s", s.addr) - - var nm sync.Map - buf := make([]byte, proxy.UDPBufSize) - - for { - c := NewPktConn(lc, nil, nil, true, nil) - - n, raddr, err := c.ReadFrom(buf) - if err != nil { - log.F("[socks5u] 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("[socks5u] can not get target address, not a valid request") - continue - } - - lpc, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String()) - if err != nil { - log.F("[socks5u] remote dial error: %v", err) - continue - } - - pc = NewPktConn(lpc, nextHop, nil, false, nil) - nm.Store(raddr.String(), pc) - - go func() { - proxy.RelayUDP(c, raddr, pc, 2*time.Minute) - pc.Close() - nm.Delete(raddr.String()) - }() - - log.F("[socks5u] %s <-> %s", raddr, c.tgtAddr) - - } else { - pc = v.(*PktConn) - } - - _, err = pc.WriteTo(buf[:n], pc.writeAddr) - if err != nil { - log.F("[socks5u] remote write error: %v", err) - continue - } - - // log.F("[socks5u] %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 -} - -// 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("[socks5]: 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{Version, 1, 0}) - - buf := pool.GetBuffer(socks.MaxAddrLen) - defer pool.PutBuffer(buf) - - // 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{Version, 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] != Version { - 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 s.user != "" && s.password != "" { - _, err := rw.Write([]byte{Version, socks.AuthPassword}) - if err != nil { - return nil, err - } - - _, err = io.ReadFull(rw, buf[:2]) - if err != nil { - return nil, err - } - - // Get username - userLen := int(buf[1]) - if userLen <= 0 { - rw.Write([]byte{1, 1}) - return nil, errors.New("auth failed: wrong username length") - } - - if _, err := io.ReadFull(rw, buf[:userLen]); err != nil { - return nil, errors.New("auth failed: cannot get username") - } - user := string(buf[:userLen]) - - // Get password - _, err = rw.Read(buf[:1]) - if err != nil { - return nil, errors.New("auth failed: cannot get password len") - } - - passLen := int(buf[0]) - if passLen <= 0 { - rw.Write([]byte{1, 1}) - return nil, errors.New("auth failed: wrong password length") - } - - _, err = io.ReadFull(rw, buf[:passLen]) - if err != nil { - return nil, errors.New("auth failed: cannot get password") - } - pass := string(buf[:passLen]) - - // Verify - if user != s.user || pass != s.password { - _, err = rw.Write([]byte{1, 1}) - if err != nil { - return nil, err - } - return nil, errors.New("auth failed, authinfo: " + user + ":" + pass) - } - - // Response auth state - _, err = rw.Write([]byte{1, 0}) - if err != nil { - return nil, err - } - - } else if _, err := rw.Write([]byte{Version, socks.AuthNone}); 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/client.go b/proxy/ss/client.go new file mode 100644 index 0000000..053bf1c --- /dev/null +++ b/proxy/ss/client.go @@ -0,0 +1,62 @@ +package ss + +import ( + "errors" + "net" + + "github.com/nadoo/glider/log" + "github.com/nadoo/glider/proxy" + "github.com/nadoo/glider/proxy/socks" +) + +// NewSSDialer returns a ss proxy dialer. +func NewSSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) { + return NewSS(s, d, nil) +} + +// Addr returns forwarder's address. +func (s *SS) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} + +// 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/ss/server.go b/proxy/ss/server.go new file mode 100644 index 0000000..953bc2d --- /dev/null +++ b/proxy/ss/server.go @@ -0,0 +1,178 @@ +package ss + +import ( + "net" + "strings" + "sync" + "time" + + "github.com/nadoo/glider/log" + "github.com/nadoo/glider/pool" + "github.com/nadoo/glider/proxy" + "github.com/nadoo/glider/proxy/socks" +) + +// NewSSServer returns a ss proxy server. +func NewSSServer(s string, p proxy.Proxy) (proxy.Server, error) { + return NewSS(s, nil, p) +} + +// 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.Serve(c) + } + +} + +// Serve serves a connection. +func (s *SS) Serve(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.proxy.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("[ssuot] UDP remote listen error: %v", err) + } + defer rc.Close() + + buf := pool.GetBuffer(proxy.UDPBufSize) + defer pool.PutBuffer(buf) + + n, err := c.Read(buf) + if err != nil { + log.F("[ssuot] error in read: %s\n", err) + return + } + + tgtAddr, _ := net.ResolveUDPAddr("udp", tgt.String()) + rc.WriteTo(buf[:n], tgtAddr) + + n, _, err = rc.ReadFrom(buf) + if err != nil { + log.F("[ssuot] 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] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + return + } + defer rc.Close() + + log.F("[ss] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr()) + + if err = proxy.Relay(c, rc); err != nil { + log.F("[ss] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + // record remote conn failure only + if !strings.Contains(err.Error(), s.addr) { + s.proxy.Record(dialer, false) + } + } +} + +// ListenAndServeUDP serves udp ss requests. +func (s *SS) ListenAndServeUDP() { + lc, err := net.ListenPacket("udp", s.addr) + if err != nil { + log.F("[ss] failed to listen on UDP %s: %v", s.addr, err) + return + } + defer lc.Close() + + lc = s.PacketConn(lc) + + log.F("[ss] listening UDP on %s", s.addr) + + var nm sync.Map + buf := make([]byte, proxy.UDPBufSize) + + for { + c := NewPktConn(lc, nil, nil, true) + + n, raddr, err := c.ReadFrom(buf) + if err != nil { + log.F("[ssu] remote read error: %v", err) + continue + } + + var pc *PktConn + v, ok := nm.Load(raddr.String()) + if !ok && v == nil { + lpc, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String()) + if err != nil { + log.F("[ssu] remote dial error: %v", err) + continue + } + + pc = NewPktConn(lpc, nextHop, nil, false) + nm.Store(raddr.String(), pc) + + go func() { + proxy.RelayUDP(c, raddr, pc, 2*time.Minute) + pc.Close() + nm.Delete(raddr.String()) + }() + + log.F("[ssu] %s <-> %s", raddr, c.tgtAddr) + + } else { + pc = v.(*PktConn) + } + + _, err = pc.WriteTo(buf[:n], pc.writeAddr) + if err != nil { + log.F("[ssu] remote write error: %v", err) + continue + } + + // log.F("[ssu] %s <-> %s", raddr, c.tgtAddr) + } +} diff --git a/proxy/ss/ss.go b/proxy/ss/ss.go index 7c836fc..81b0a63 100644 --- a/proxy/ss/ss.go +++ b/proxy/ss/ss.go @@ -1,19 +1,13 @@ package ss import ( - "errors" - "net" "net/url" "strings" - "sync" - "time" "github.com/nadoo/go-shadowsocks2/core" "github.com/nadoo/glider/log" - "github.com/nadoo/glider/pool" "github.com/nadoo/glider/proxy" - "github.com/nadoo/glider/proxy/socks" ) // SS is a base ss struct. @@ -57,224 +51,7 @@ func NewSS(s string, d proxy.Dialer, p proxy.Proxy) (*SS, error) { return ss, nil } -// NewSSDialer returns a ss proxy dialer. -func NewSSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) { - return NewSS(s, d, nil) -} - -// NewSSServer returns a ss proxy server. -func NewSSServer(s string, p proxy.Proxy) (proxy.Server, error) { - return NewSS(s, nil, p) -} - -// 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.Serve(c) - } - -} - -// Serve serves a connection. -func (s *SS) Serve(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.proxy.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("[ssuot] UDP remote listen error: %v", err) - } - defer rc.Close() - - buf := pool.GetBuffer(proxy.UDPBufSize) - defer pool.PutBuffer(buf) - - n, err := c.Read(buf) - if err != nil { - log.F("[ssuot] error in read: %s\n", err) - return - } - - tgtAddr, _ := net.ResolveUDPAddr("udp", tgt.String()) - rc.WriteTo(buf[:n], tgtAddr) - - n, _, err = rc.ReadFrom(buf) - if err != nil { - log.F("[ssuot] 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] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) - return - } - defer rc.Close() - - log.F("[ss] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr()) - - if err = proxy.Relay(c, rc); err != nil { - log.F("[ss] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) - // record remote conn failure only - if !strings.Contains(err.Error(), s.addr) { - s.proxy.Record(dialer, false) - } - } -} - -// ListenAndServeUDP serves udp ss requests. -func (s *SS) ListenAndServeUDP() { - lc, err := net.ListenPacket("udp", s.addr) - if err != nil { - log.F("[ss] failed to listen on UDP %s: %v", s.addr, err) - return - } - defer lc.Close() - - lc = s.PacketConn(lc) - - log.F("[ss] listening UDP on %s", s.addr) - - var nm sync.Map - buf := make([]byte, proxy.UDPBufSize) - - for { - c := NewPktConn(lc, nil, nil, true) - - n, raddr, err := c.ReadFrom(buf) - if err != nil { - log.F("[ssu] remote read error: %v", err) - continue - } - - var pc *PktConn - v, ok := nm.Load(raddr.String()) - if !ok && v == nil { - lpc, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String()) - if err != nil { - log.F("[ssu] remote dial error: %v", err) - continue - } - - pc = NewPktConn(lpc, nextHop, nil, false) - nm.Store(raddr.String(), pc) - - go func() { - proxy.RelayUDP(c, raddr, pc, 2*time.Minute) - pc.Close() - nm.Delete(raddr.String()) - }() - - log.F("[ssu] %s <-> %s", raddr, c.tgtAddr) - - } else { - pc = v.(*PktConn) - } - - _, err = pc.WriteTo(buf[:n], pc.writeAddr) - if err != nil { - log.F("[ssu] remote write error: %v", err) - continue - } - - // log.F("[ssu] %s <-> %s", raddr, c.tgtAddr) - } -} - // ListCipher returns all the ciphers supported. 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 -} - -// 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/vless/server.go b/proxy/vless/server.go index 54b556f..58b0fb9 100644 --- a/proxy/vless/server.go +++ b/proxy/vless/server.go @@ -49,7 +49,7 @@ func (s *VLess) Serve(c net.Conn) { } c = NewServerConn(c) - _, err := s.readHeader(c) + cmd, err := s.readHeader(c) if err != nil { log.F("[vless] verify header error: %v", err) return @@ -61,6 +61,16 @@ func (s *VLess) Serve(c net.Conn) { return } + switch cmd { + case CmdTCP: + s.ServeTCP(c, tgt) + case CmdUDP: + s.ServeUOT(c, tgt) + } +} + +// ServeTCP serves tcp requests. +func (s *VLess) ServeTCP(c net.Conn, tgt string) { dialer := s.proxy.NextDialer(tgt) rc, err := dialer.Dial("tcp", tgt) if err != nil { @@ -80,6 +90,10 @@ func (s *VLess) Serve(c net.Conn) { } } +// ServeUOT serves udp over tcp requests. +func (s *VLess) ServeUOT(c net.Conn, tgt string) { +} + func (s *VLess) readHeader(r io.Reader) (CmdType, error) { buf := pool.GetBuffer(16) defer pool.PutBuffer(buf)