mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 17:35:40 +08:00
socks5: support udp in server mode
This commit is contained in:
parent
601e0ddcda
commit
6b4d2582ca
@ -21,7 +21,7 @@ Listen(local proxy server):
|
||||
- SS proxy
|
||||
- Linux transparent proxy(iptables redirect)
|
||||
- TCP tunnel
|
||||
- DNS Tunnel(udp2tcp)
|
||||
- UDP over TCP tunnel
|
||||
|
||||
Forward(upstream proxy server):
|
||||
- Socks5 proxy
|
||||
@ -47,7 +47,9 @@ General:
|
||||
- Rule proxy based on destinations: [Config Examples](config/examples)
|
||||
|
||||
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)
|
||||
- [ ] DNS Cache
|
||||
- [ ] TUN/TAP device support
|
||||
|
2
http.go
2
http.go
@ -249,7 +249,7 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||
return nil, nil, errors.New("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.
|
||||
|
15
mixed.go
15
mixed.go
@ -41,18 +41,21 @@ func NewMixedProxy(addr, user, pass, rawQuery string, sDialer Dialer) (*MixedPro
|
||||
|
||||
// ListenAndServe .
|
||||
func (p *MixedProxy) ListenAndServe() {
|
||||
|
||||
go p.socks5.ListenAndServeUDP()
|
||||
|
||||
l, err := net.Listen("tcp", p.addr)
|
||||
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
|
||||
}
|
||||
|
||||
logf("listening TCP on %s", p.addr)
|
||||
logf("proxy-mixed listening TCP on %s", p.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
logf("failed to accept: %v", err)
|
||||
logf("proxy-mixed failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -73,13 +76,13 @@ func (p *MixedProxy) Serve(conn net.Conn) {
|
||||
if p.socks5 != nil {
|
||||
head, err := c.Peek(1)
|
||||
if err != nil {
|
||||
logf("peek error: %s", err)
|
||||
logf("proxy-mixed peek error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// check socks5, client send socksversion: 5 as the first byte
|
||||
if head[0] == socks5Version {
|
||||
p.socks5.Serve(c)
|
||||
p.socks5.ServeTCP(c)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -87,7 +90,7 @@ func (p *MixedProxy) Serve(conn net.Conn) {
|
||||
if p.http != nil {
|
||||
head, err := c.Peek(8)
|
||||
if err != nil {
|
||||
logf("peek error: %s", err)
|
||||
logf("proxy-mixed peek error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
196
socks5.go
196
socks5.go
@ -14,6 +14,8 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
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.
|
||||
type Addr []byte
|
||||
|
||||
var socks5Errors = []string{
|
||||
"",
|
||||
"general failure",
|
||||
"connection forbidden",
|
||||
"network unreachable",
|
||||
"host unreachable",
|
||||
"connection refused",
|
||||
"TTL expired",
|
||||
"command not supported",
|
||||
"address type not supported",
|
||||
var socks5Errors = []error{
|
||||
errors.New(""),
|
||||
errors.New("general failure"),
|
||||
errors.New("connection forbidden"),
|
||||
errors.New("network unreachable"),
|
||||
errors.New("host unreachable"),
|
||||
errors.New("connection refused"),
|
||||
errors.New("TTL expired"),
|
||||
errors.New("command not supported"),
|
||||
errors.New("address type not supported"),
|
||||
errors.New("socks5UDPAssociate"),
|
||||
}
|
||||
|
||||
// SOCKS5 struct
|
||||
@ -77,8 +80,14 @@ func NewSOCKS5(addr, user, pass string, cDialer Dialer, sDialer Dialer) (*SOCKS5
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// ListenAndServe .
|
||||
// ListenAndServe serves socks5 requests.
|
||||
func (s *SOCKS5) ListenAndServe() {
|
||||
go s.ListenAndServeUDP()
|
||||
s.ListenAndServeTCP()
|
||||
}
|
||||
|
||||
// ListenAndServeTCP .
|
||||
func (s *SOCKS5) ListenAndServeTCP() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
logf("proxy-socks5 failed to listen on %s: %v", s.addr, err)
|
||||
@ -94,12 +103,12 @@ func (s *SOCKS5) ListenAndServe() {
|
||||
continue
|
||||
}
|
||||
|
||||
go s.Serve(c)
|
||||
go s.ServeTCP(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve .
|
||||
func (s *SOCKS5) Serve(c net.Conn) {
|
||||
// ServeTCP .
|
||||
func (s *SOCKS5) ServeTCP(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
@ -108,6 +117,20 @@ func (s *SOCKS5) Serve(c net.Conn) {
|
||||
|
||||
tgt, err := s.handshake(c)
|
||||
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)
|
||||
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.
|
||||
func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) {
|
||||
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.
|
||||
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,
|
||||
@ -254,7 +333,7 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error {
|
||||
|
||||
failure := "unknown error"
|
||||
if int(buf[1]) < len(socks5Errors) {
|
||||
failure = socks5Errors[buf[1]]
|
||||
failure = socks5Errors[buf[1]].Error()
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
if buf[1] != socks5Connect {
|
||||
return nil, errors.New(socks5Errors[7])
|
||||
}
|
||||
cmd := buf[1]
|
||||
addr, err := readAddr(rw, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// write VER REP RSV ATYP BND.ADDR BND.PORT
|
||||
_, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0})
|
||||
return addr, err
|
||||
switch cmd {
|
||||
case socks5Connect:
|
||||
_, 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.
|
||||
@ -380,7 +469,7 @@ func readAddr(r io.Reader, b []byte) (Addr, error) {
|
||||
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.
|
||||
@ -453,3 +542,64 @@ func ParseAddr(s string) 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
2
ss.go
@ -243,7 +243,7 @@ func (s *SS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return pkc, nextHop, err
|
||||
}
|
||||
|
||||
// PktConn wraps a net.PacketConn and support Write method like net.Conn
|
||||
// PktConn .
|
||||
type PktConn struct {
|
||||
net.PacketConn
|
||||
|
||||
|
@ -59,6 +59,7 @@ func (s *UDPTun) ListenAndServe() {
|
||||
}
|
||||
|
||||
nm.Store(raddr.String(), pc)
|
||||
|
||||
go func() {
|
||||
timedCopy(c, raddr, pc, 2*time.Minute)
|
||||
pc.Close()
|
||||
|
Loading…
Reference in New Issue
Block a user