socks5: support udp in server mode

This commit is contained in:
nadoo 2018-01-20 22:13:04 +08:00
parent 601e0ddcda
commit 6b4d2582ca
7 changed files with 195 additions and 34 deletions

View File

@ -21,7 +21,7 @@ Listen(local proxy server):
- SS proxy - SS proxy
- Linux transparent proxy(iptables redirect) - Linux transparent proxy(iptables redirect)
- TCP tunnel - TCP tunnel
- DNS Tunnel(udp2tcp) - UDP over TCP tunnel
Forward(upstream proxy server): Forward(upstream proxy server):
- Socks5 proxy - Socks5 proxy
@ -47,7 +47,9 @@ General:
- Rule proxy based on destinations: [Config Examples](config/examples) - Rule proxy based on destinations: [Config Examples](config/examples)
TODO: TODO:
- [x] UDP over TCP Tunnel (client <--udp--> glider/uottun <--tcp--> ss <--udp--> target)
- [x] UDP tunnel
- [x] UDP over TCP Tunnel (client <--udp--> glider/uottun <--tcp--> glider/ss <--udp--> target)
- [ ] Transparent UDP proxy (iptables tproxy) - [ ] Transparent UDP proxy (iptables tproxy)
- [ ] DNS Cache - [ ] DNS Cache
- [ ] TUN/TAP device support - [ ] TUN/TAP device support

View File

@ -249,7 +249,7 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
// DialUDP connects to the given address via the proxy. // DialUDP connects to the given address via the proxy.
func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
return nil, nil, errors.New("DialUDP not supported") 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. // parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts.

View File

@ -41,18 +41,21 @@ func NewMixedProxy(addr, user, pass, rawQuery string, sDialer Dialer) (*MixedPro
// ListenAndServe . // ListenAndServe .
func (p *MixedProxy) ListenAndServe() { func (p *MixedProxy) ListenAndServe() {
go p.socks5.ListenAndServeUDP()
l, err := net.Listen("tcp", p.addr) l, err := net.Listen("tcp", p.addr)
if err != nil { if err != nil {
logf("failed to listen on %s: %v", p.addr, err) logf("proxy-mixed failed to listen on %s: %v", p.addr, err)
return return
} }
logf("listening TCP on %s", p.addr) logf("proxy-mixed listening TCP on %s", p.addr)
for { for {
c, err := l.Accept() c, err := l.Accept()
if err != nil { if err != nil {
logf("failed to accept: %v", err) logf("proxy-mixed failed to accept: %v", err)
continue continue
} }
@ -73,13 +76,13 @@ func (p *MixedProxy) Serve(conn net.Conn) {
if p.socks5 != nil { if p.socks5 != nil {
head, err := c.Peek(1) head, err := c.Peek(1)
if err != nil { if err != nil {
logf("peek error: %s", err) logf("proxy-mixed peek error: %s", err)
return return
} }
// check socks5, client send socksversion: 5 as the first byte // check socks5, client send socksversion: 5 as the first byte
if head[0] == socks5Version { if head[0] == socks5Version {
p.socks5.Serve(c) p.socks5.ServeTCP(c)
return return
} }
} }
@ -87,7 +90,7 @@ func (p *MixedProxy) Serve(conn net.Conn) {
if p.http != nil { if p.http != nil {
head, err := c.Peek(8) head, err := c.Peek(8)
if err != nil { if err != nil {
logf("peek error: %s", err) logf("proxy-mixed peek error: %s", err)
return return
} }

196
socks5.go
View File

@ -14,6 +14,8 @@ import (
"io" "io"
"net" "net"
"strconv" "strconv"
"sync"
"time"
) )
const socks5Version = 5 const socks5Version = 5
@ -43,16 +45,17 @@ const MaxAddrLen = 1 + 1 + 255 + 2
// Addr represents a SOCKS address as defined in RFC 1928 section 5. // Addr represents a SOCKS address as defined in RFC 1928 section 5.
type Addr []byte type Addr []byte
var socks5Errors = []string{ var socks5Errors = []error{
"", errors.New(""),
"general failure", errors.New("general failure"),
"connection forbidden", errors.New("connection forbidden"),
"network unreachable", errors.New("network unreachable"),
"host unreachable", errors.New("host unreachable"),
"connection refused", errors.New("connection refused"),
"TTL expired", errors.New("TTL expired"),
"command not supported", errors.New("command not supported"),
"address type not supported", errors.New("address type not supported"),
errors.New("socks5UDPAssociate"),
} }
// SOCKS5 struct // SOCKS5 struct
@ -77,8 +80,14 @@ func NewSOCKS5(addr, user, pass string, cDialer Dialer, sDialer Dialer) (*SOCKS5
return s, nil return s, nil
} }
// ListenAndServe . // ListenAndServe serves socks5 requests.
func (s *SOCKS5) ListenAndServe() { func (s *SOCKS5) ListenAndServe() {
go s.ListenAndServeUDP()
s.ListenAndServeTCP()
}
// ListenAndServeTCP .
func (s *SOCKS5) ListenAndServeTCP() {
l, err := net.Listen("tcp", s.addr) l, err := net.Listen("tcp", s.addr)
if err != nil { if err != nil {
logf("proxy-socks5 failed to listen on %s: %v", s.addr, err) logf("proxy-socks5 failed to listen on %s: %v", s.addr, err)
@ -94,12 +103,12 @@ func (s *SOCKS5) ListenAndServe() {
continue continue
} }
go s.Serve(c) go s.ServeTCP(c)
} }
} }
// Serve . // ServeTCP .
func (s *SOCKS5) Serve(c net.Conn) { func (s *SOCKS5) ServeTCP(c net.Conn) {
defer c.Close() defer c.Close()
if c, ok := c.(*net.TCPConn); ok { if c, ok := c.(*net.TCPConn); ok {
@ -108,6 +117,20 @@ func (s *SOCKS5) Serve(c net.Conn) {
tgt, err := s.handshake(c) tgt, err := s.handshake(c)
if err != nil { if err != nil {
// UDP: keep the connection until disconnect then free the UDP socket
if err == socks5Errors[9] {
buf := []byte{}
// block here
for {
_, err := c.Read(buf)
if err, ok := err.(net.Error); ok && err.Timeout() {
continue
}
logf("UDP Associate End.")
return
}
}
logf("proxy-socks5 failed to get target address: %v", err) logf("proxy-socks5 failed to get target address: %v", err)
return return
} }
@ -130,6 +153,62 @@ func (s *SOCKS5) Serve(c net.Conn) {
} }
} }
// ListenAndServeUDP serves udp requests.
func (s *SOCKS5) ListenAndServeUDP() {
lc, err := net.ListenPacket("udp", s.addr)
if err != nil {
logf("proxy-socks5-udp failed to listen on %s: %v", s.addr, err)
return
}
defer lc.Close()
logf("proxy-socks5-udp listening UDP on %s", s.addr)
var nm sync.Map
buf := make([]byte, udpBufSize)
for {
c := NewSocks5PktConn(lc, nil, nil, true)
n, raddr, err := c.ReadFrom(buf)
if err != nil {
logf("proxy-socks5-udp remote read error: %v", err)
continue
}
var pc *Socks5PktConn
v, ok := nm.Load(raddr.String())
if !ok && v == nil {
lpc, nextHop, err := s.sDialer.DialUDP("udp", c.tgtAddr.String())
if err != nil {
logf("proxy-socks5-udp remote dial error: %v", err)
continue
}
pc = NewSocks5PktConn(lpc, nextHop, nil, false)
nm.Store(raddr.String(), pc)
go func() {
timedCopy(c, raddr, pc, 2*time.Minute)
pc.Close()
nm.Delete(raddr.String())
}()
} else {
pc = v.(*Socks5PktConn)
}
_, err = pc.WriteTo(buf[:n], pc.writeAddr)
if err != nil {
logf("proxy-socks5-udp remote write error: %v", err)
continue
}
logf("proxy-socks5-udp %s <-> %s", raddr, c.tgtAddr)
}
}
// Dial connects to the address addr on the network net via the SOCKS5 proxy. // Dial connects to the address addr on the network net via the SOCKS5 proxy.
func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) { func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) {
switch network { switch network {
@ -158,7 +237,7 @@ func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) {
// DialUDP connects to the given address via the proxy. // DialUDP connects to the given address via the proxy.
func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
return nil, nil, errors.New("DialUDP not supported") return nil, nil, errors.New("sock5 client does not support udp now")
} }
// connect takes an existing connection to a socks5 proxy server, // connect takes an existing connection to a socks5 proxy server,
@ -254,7 +333,7 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error {
failure := "unknown error" failure := "unknown error"
if int(buf[1]) < len(socks5Errors) { if int(buf[1]) < len(socks5Errors) {
failure = socks5Errors[buf[1]] failure = socks5Errors[buf[1]].Error()
} }
if len(failure) > 0 { if len(failure) > 0 {
@ -314,16 +393,26 @@ func (s *SOCKS5) handshake(rw io.ReadWriter) (Addr, error) {
if _, err := io.ReadFull(rw, buf[:3]); err != nil { if _, err := io.ReadFull(rw, buf[:3]); err != nil {
return nil, err return nil, err
} }
if buf[1] != socks5Connect { cmd := buf[1]
return nil, errors.New(socks5Errors[7])
}
addr, err := readAddr(rw, buf) addr, err := readAddr(rw, buf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// write VER REP RSV ATYP BND.ADDR BND.PORT switch cmd {
_, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) case socks5Connect:
return addr, err _, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) // SOCKS v5, reply succeeded
case socks5UDPAssociate:
listenAddr := ParseAddr(rw.(net.Conn).LocalAddr().String())
_, err = rw.Write(append([]byte{5, 0, 0}, listenAddr...)) // SOCKS v5, reply succeeded
if err != nil {
return nil, socks5Errors[7]
}
err = socks5Errors[9]
default:
return nil, socks5Errors[7]
}
return addr, err // skip VER, CMD, RSV fields
} }
// String serializes SOCKS address a to string form. // String serializes SOCKS address a to string form.
@ -380,7 +469,7 @@ func readAddr(r io.Reader, b []byte) (Addr, error) {
return b[:1+net.IPv6len+2], err return b[:1+net.IPv6len+2], err
} }
return nil, errors.New(socks5Errors[8]) return nil, socks5Errors[8]
} }
// ReadAddr reads just enough bytes from r to get a valid Addr. // ReadAddr reads just enough bytes from r to get a valid Addr.
@ -453,3 +542,64 @@ func ParseAddr(s string) Addr {
return addr return addr
} }
// Socks5PktConn .
type Socks5PktConn struct {
net.PacketConn
writeAddr net.Addr // write to and read from addr
tgtAddr Addr
tgtHeader bool
}
// NewSocks5PktConn returns a Socks5PktConn
func NewSocks5PktConn(c net.PacketConn, writeAddr net.Addr, tgtAddr Addr, tgtHeader bool) *Socks5PktConn {
pc := &Socks5PktConn{
PacketConn: c,
writeAddr: writeAddr,
tgtAddr: tgtAddr,
tgtHeader: tgtHeader}
return pc
}
func (pc *Socks5PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
if !pc.tgtHeader {
return pc.PacketConn.ReadFrom(b)
}
buf := make([]byte, len(b))
n, raddr, err := pc.PacketConn.ReadFrom(buf)
if err != nil {
return n, raddr, err
}
// https://tools.ietf.org/html/rfc1928#section-7
// +----+------+------+----------+----------+----------+
// |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
// +----+------+------+----------+----------+----------+
// | 2 | 1 | 1 | Variable | 2 | Variable |
// +----+------+------+----------+----------+----------+
tgtAddr := SplitAddr(buf[3:])
copy(b, buf[3+len(tgtAddr):])
//test
if pc.writeAddr == nil {
pc.writeAddr = raddr
}
if pc.tgtAddr == nil {
pc.tgtAddr = tgtAddr
}
return n - len(tgtAddr), raddr, err
}
func (pc *Socks5PktConn) WriteTo(b []byte, addr net.Addr) (int, error) {
if !pc.tgtHeader {
return pc.PacketConn.WriteTo(b, addr)
}
buf := append(append([]byte{0, 0, 0}, b[:]...))
return pc.PacketConn.WriteTo(buf, pc.writeAddr)
}

2
ss.go
View File

@ -243,7 +243,7 @@ func (s *SS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
return pkc, nextHop, err return pkc, nextHop, err
} }
// PktConn wraps a net.PacketConn and support Write method like net.Conn // PktConn .
type PktConn struct { type PktConn struct {
net.PacketConn net.PacketConn

View File

@ -59,6 +59,7 @@ func (s *UDPTun) ListenAndServe() {
} }
nm.Store(raddr.String(), pc) nm.Store(raddr.String(), pc)
go func() { go func() {
timedCopy(c, raddr, pc, 2*time.Minute) timedCopy(c, raddr, pc, 2*time.Minute)
pc.Close() pc.Close()

View File

@ -69,7 +69,12 @@ func (s *UoTTun) ListenAndServe() {
c.WriteTo(resp, clientAddr) c.WriteTo(resp, clientAddr)
}() }()
rc.Write(buf[:n]) _, err = rc.Write(buf[:n])
if err != nil {
logf("proxy-uottun remote write error: %v", err)
continue
}
logf("proxy-uottun %s <-> %s", clientAddr, s.raddr) logf("proxy-uottun %s <-> %s", clientAddr, s.raddr)
} }
} }