mirror of
https://github.com/nadoo/glider.git
synced 2025-02-24 01:45:39 +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
|
- 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
|
||||||
|
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.
|
// 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.
|
||||||
|
15
mixed.go
15
mixed.go
@ -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
196
socks5.go
@ -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
2
ss.go
@ -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
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user