mirror of
https://github.com/nadoo/glider.git
synced 2026-06-26 16:40:12 +08:00
anytls: support udp
This commit is contained in:
parent
533571f1ec
commit
86f9f078fd
10
README.md
10
README.md
@ -52,11 +52,11 @@ we can set up local listeners as proxy servers, and forward requests to internet
|
||||
|HTTP |√| |√| |client & server
|
||||
|SOCKS5 |√|√|√|√|client & server
|
||||
|SS |√|√|√|√|client & server
|
||||
|Trojan |√|√|√|√|client & server
|
||||
|Trojanc |√|√|√|√|trojan cleartext(without tls)
|
||||
|AnyTLS |√| |√| |client & server
|
||||
|AnyTLSc |√| |√| |anytls cleartext(without tls)
|
||||
|VLESS |√|√|√|√|client & server
|
||||
|Trojan |√| |√|√|client & server
|
||||
|Trojanc |√| |√|√|trojan cleartext(without tls)
|
||||
|AnyTLS |√| |√|√|client & server
|
||||
|AnyTLSc |√| |√|√|anytls cleartext(without tls)
|
||||
|VLESS |√| |√|√|client & server
|
||||
|VMess | | |√|√|client only
|
||||
|SSR | | |√| |client only
|
||||
|SSH | | |√| |client only
|
||||
|
||||
@ -64,7 +64,42 @@ func (s *AnyTLS) Dial(network, addr string) (net.Conn, error) {
|
||||
}
|
||||
|
||||
func (s *AnyTLS) DialUDP(network, addr string) (net.PacketConn, error) {
|
||||
return nil, proxy.ErrNotSupported
|
||||
if network != "udp" && network != "udp4" && network != "udp6" {
|
||||
return nil, proxy.ErrNotSupported
|
||||
}
|
||||
target := socks.ParseAddr(addr)
|
||||
if target == nil {
|
||||
return nil, fmt.Errorf("[anytls] invalid target address: %s", addr)
|
||||
}
|
||||
raw := socks.ParseAddr(net.JoinHostPort(uotV2MagicHost, "0"))
|
||||
if raw == nil {
|
||||
return nil, fmt.Errorf("[anytls] invalid udp-over-tcp target address")
|
||||
}
|
||||
ss, err := s.newClientSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st, err := ss.openStream()
|
||||
if err != nil {
|
||||
_ = ss.Close()
|
||||
return nil, err
|
||||
}
|
||||
if _, err := st.Write(raw); err != nil {
|
||||
_ = st.Close()
|
||||
_ = ss.Close()
|
||||
return nil, err
|
||||
}
|
||||
if err := writeUOTV2Request(st, target); err != nil {
|
||||
_ = st.Close()
|
||||
_ = ss.Close()
|
||||
return nil, err
|
||||
}
|
||||
if err := ss.waitSYNACK(st.id, s.synackTimeout); err != nil {
|
||||
_ = st.Close()
|
||||
_ = ss.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &clientPacketConn{PacketConn: newUOTPacketConn(st, target), session: ss}, nil
|
||||
}
|
||||
|
||||
func (s *AnyTLS) newClientSession() (*session, error) {
|
||||
@ -105,3 +140,14 @@ func (c *clientConn) Close() error {
|
||||
_ = c.session.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
type clientPacketConn struct {
|
||||
net.PacketConn
|
||||
session *session
|
||||
}
|
||||
|
||||
func (c *clientPacketConn) Close() error {
|
||||
err := c.PacketConn.Close()
|
||||
_ = c.session.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
95
proxy/anytls/packet.go
Normal file
95
proxy/anytls/packet.go
Normal file
@ -0,0 +1,95 @@
|
||||
package anytls
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/pkg/pool"
|
||||
"github.com/nadoo/glider/pkg/socks"
|
||||
)
|
||||
|
||||
const uotV2MagicHost = "sp.v2.udp-over-tcp.arpa"
|
||||
|
||||
// uotPacketConn carries UDP packets over an AnyTLS stream using sing-box
|
||||
// udp-over-tcp v2 connect format.
|
||||
type uotPacketConn struct {
|
||||
net.Conn
|
||||
target socks.Addr
|
||||
}
|
||||
|
||||
func newUOTPacketConn(c net.Conn, target socks.Addr) *uotPacketConn {
|
||||
return &uotPacketConn{Conn: c, target: target}
|
||||
}
|
||||
|
||||
func (pc *uotPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
if len(b) < 2 {
|
||||
return 0, pc.target, errors.New("buf size is not enough")
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(pc.Conn, b[:2]); err != nil {
|
||||
return 0, pc.target, err
|
||||
}
|
||||
length := int(binary.BigEndian.Uint16(b[:2]))
|
||||
if len(b) < length {
|
||||
return 0, pc.target, errors.New("buf size is not enough")
|
||||
}
|
||||
|
||||
n, err := io.ReadFull(pc.Conn, b[:length])
|
||||
return n, pc.target, err
|
||||
}
|
||||
|
||||
func (pc *uotPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
buf := pool.GetBytesBuffer()
|
||||
defer pool.PutBytesBuffer(buf)
|
||||
|
||||
var head [2]byte
|
||||
binary.BigEndian.PutUint16(head[:], uint16(len(b)))
|
||||
buf.Write(head[:])
|
||||
buf.Write(b)
|
||||
|
||||
n, err := pc.Write(buf.Bytes())
|
||||
if n > 2 {
|
||||
return n - 2, err
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (pc *uotPacketConn) SetDeadline(t time.Time) error {
|
||||
return pc.Conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (pc *uotPacketConn) SetReadDeadline(t time.Time) error {
|
||||
return pc.Conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (pc *uotPacketConn) SetWriteDeadline(t time.Time) error {
|
||||
return pc.Conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func writeUOTV2Request(w io.Writer, target socks.Addr) error {
|
||||
if target == nil {
|
||||
return errors.New("invalid target address")
|
||||
}
|
||||
|
||||
buf := pool.GetBytesBuffer()
|
||||
defer pool.PutBytesBuffer(buf)
|
||||
|
||||
buf.WriteByte(1) // connect stream format
|
||||
buf.Write(target)
|
||||
_, err := w.Write(buf.Bytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func readUOTV2Request(r io.Reader) (socks.Addr, error) {
|
||||
var connect [1]byte
|
||||
if _, err := io.ReadFull(r, connect[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if connect[0] != 1 {
|
||||
return nil, errors.New("udp-over-tcp v2 non-connect format is not supported")
|
||||
}
|
||||
return socks.ReadAddr(r)
|
||||
}
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/pkg/log"
|
||||
"github.com/nadoo/glider/pkg/socks"
|
||||
@ -127,6 +128,11 @@ func (s *AnyTLS) serveStream(ss *session, st *stream) {
|
||||
log.F("[anytls] read target error: %v", err)
|
||||
return
|
||||
}
|
||||
host, _, err := net.SplitHostPort(target.String())
|
||||
if err == nil && host == uotV2MagicHost {
|
||||
s.serveUoT(ss, st)
|
||||
return
|
||||
}
|
||||
|
||||
rc, dialer, err := s.proxy.Dial("tcp", target.String())
|
||||
if err != nil {
|
||||
@ -145,3 +151,32 @@ func (s *AnyTLS) serveStream(ss *session, st *stream) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AnyTLS) serveUoT(ss *session, st *stream) {
|
||||
target, err := readUOTV2Request(st)
|
||||
if err != nil {
|
||||
_ = ss.writeFrame(frame{command: cmdSYNACK, streamID: st.id, data: []byte(err.Error())})
|
||||
log.F("[anytls] read udp-over-tcp request error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
dstPC, dialer, err := s.proxy.DialUDP("udp", target.String())
|
||||
if err != nil {
|
||||
_ = ss.writeFrame(frame{command: cmdSYNACK, streamID: st.id, data: []byte(err.Error())})
|
||||
log.F("[anytls] %s <-UoT-> %s via %s, error in dial: %v", st.RemoteAddr(), target, dialer.Addr(), err)
|
||||
return
|
||||
}
|
||||
defer dstPC.Close()
|
||||
|
||||
_ = ss.writeFrame(frame{command: cmdSYNACK, streamID: st.id})
|
||||
pc := newUOTPacketConn(st, target)
|
||||
log.F("[anytls] %s <-UoT-> %s via %s", st.RemoteAddr(), target, dialer.Addr())
|
||||
|
||||
go proxy.CopyUDP(dstPC, nil, pc, 2*time.Minute, 5*time.Second)
|
||||
if err := proxy.CopyUDP(pc, nil, dstPC, 2*time.Minute, 5*time.Second); err != nil {
|
||||
log.F("[anytls] %s <-UoT-> %s via %s, relay error: %v", st.RemoteAddr(), target, dialer.Addr(), err)
|
||||
if d, ok := dialer.(proxy.Dialer); ok && !strings.Contains(err.Error(), s.addr) {
|
||||
s.proxy.Record(d, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user