chore: update doc for info and restructure codes

This commit is contained in:
nadoo 2020-10-03 22:42:34 +08:00
parent 8b6739f12c
commit cd42337169
11 changed files with 761 additions and 701 deletions

View File

@ -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) [![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) [![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. 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 |ssr | | |√| |client only
|ssh | | |√| |client only |ssh | | |√| |client only
|trojan | | |√|√|client only |trojan | | |√|√|client only
|vless |√|√|√|√|client only
|vmess | | |√| |client only |vmess | | |√| |client only
|vless | | |√|√|client only
|redir |√| | | |linux only |redir |√| | | |linux only
|redir6 |√| | | |linux only(ipv6) |redir6 |√| | | |linux only(ipv6)
|tls |√| |√| |transport client & server |tls |√| |√| |transport client & server
@ -88,7 +88,7 @@ glider -h
<summary>click to see details</summary> <summary>click to see details</summary>
```bash ```bash
./glider 0.11.0 usage: ./glider 0.11.1 usage:
-checkdisabledonly -checkdisabledonly
check disabled fowarders only check disabled fowarders only
-checkinterval int -checkinterval int
@ -139,8 +139,11 @@ glider -h
verbose mode verbose mode
Available schemes: Available schemes:
listen: mixed ss socks5 http redir redir6 tcptun udptun uottun tls unix kcp listen: mixed ss socks5 http vless 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 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 scheme:
ss://method:pass@host:port ss://method:pass@host:port

View File

@ -127,8 +127,12 @@ func usage() {
fmt.Fprintf(w, "\n") fmt.Fprintf(w, "\n")
fmt.Fprintf(w, "Available schemes:\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, " 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 vmess vless trojan tls ws unix kcp simple-obfs\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, "\n")
fmt.Fprintf(w, "SS scheme:\n") fmt.Fprintf(w, "SS scheme:\n")

View File

@ -43,6 +43,9 @@ listen=http://:8080
# listen on 1080 as a socks5 proxy server. # listen on 1080 as a socks5 proxy server.
listen=socks5://:1080 listen=socks5://:1080
# listen on 1234 as vless proxy server.
# listen=vless://uuid@:1234
# listen on 1081 as a linux transparent proxy server. # listen on 1081 as a linux transparent proxy server.
# listen=redir://:1081 # listen=redir://:1081

View File

@ -18,7 +18,7 @@ import (
) )
var ( var (
version = "0.11.0" version = "0.11.1"
config = parseConfig() config = parseConfig()
) )

230
proxy/socks5/client.go Normal file
View File

@ -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
}

258
proxy/socks5/server.go Normal file
View File

@ -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
}

View File

@ -10,19 +10,10 @@
package socks5 package socks5
import ( import (
"errors"
"io"
"net"
"net/url" "net/url"
"strconv"
"strings"
"sync"
"time"
"github.com/nadoo/glider/log" "github.com/nadoo/glider/log"
"github.com/nadoo/glider/pool"
"github.com/nadoo/glider/proxy" "github.com/nadoo/glider/proxy"
"github.com/nadoo/glider/proxy/socks"
) )
// Version is socks5 version number. // Version is socks5 version number.
@ -65,463 +56,3 @@ func NewSocks5(s string, d proxy.Dialer, p proxy.Proxy) (*Socks5, error) {
return h, nil 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
}

62
proxy/ss/client.go Normal file
View File

@ -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
}

178
proxy/ss/server.go Normal file
View File

@ -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)
}
}

View File

@ -1,19 +1,13 @@
package ss package ss
import ( import (
"errors"
"net"
"net/url" "net/url"
"strings" "strings"
"sync"
"time"
"github.com/nadoo/go-shadowsocks2/core" "github.com/nadoo/go-shadowsocks2/core"
"github.com/nadoo/glider/log" "github.com/nadoo/glider/log"
"github.com/nadoo/glider/pool"
"github.com/nadoo/glider/proxy" "github.com/nadoo/glider/proxy"
"github.com/nadoo/glider/proxy/socks"
) )
// SS is a base ss struct. // 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 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. // ListCipher returns all the ciphers supported.
func ListCipher() string { func ListCipher() string {
return strings.Join(core.ListCipher(), " ") 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
}

View File

@ -49,7 +49,7 @@ func (s *VLess) Serve(c net.Conn) {
} }
c = NewServerConn(c) c = NewServerConn(c)
_, err := s.readHeader(c) cmd, err := s.readHeader(c)
if err != nil { if err != nil {
log.F("[vless] verify header error: %v", err) log.F("[vless] verify header error: %v", err)
return return
@ -61,6 +61,16 @@ func (s *VLess) Serve(c net.Conn) {
return 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) dialer := s.proxy.NextDialer(tgt)
rc, err := dialer.Dial("tcp", tgt) rc, err := dialer.Dial("tcp", tgt)
if err != nil { 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) { func (s *VLess) readHeader(r io.Reader) (CmdType, error) {
buf := pool.GetBuffer(16) buf := pool.GetBuffer(16)
defer pool.PutBuffer(buf) defer pool.PutBuffer(buf)