mirror of
https://github.com/nadoo/glider.git
synced 2026-06-27 00:50: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
|
|HTTP |√| |√| |client & server
|
||||||
|SOCKS5 |√|√|√|√|client & server
|
|SOCKS5 |√|√|√|√|client & server
|
||||||
|SS |√|√|√|√|client & server
|
|SS |√|√|√|√|client & server
|
||||||
|Trojan |√|√|√|√|client & server
|
|Trojan |√| |√|√|client & server
|
||||||
|Trojanc |√|√|√|√|trojan cleartext(without tls)
|
|Trojanc |√| |√|√|trojan cleartext(without tls)
|
||||||
|AnyTLS |√| |√| |client & server
|
|AnyTLS |√| |√|√|client & server
|
||||||
|AnyTLSc |√| |√| |anytls cleartext(without tls)
|
|AnyTLSc |√| |√|√|anytls cleartext(without tls)
|
||||||
|VLESS |√|√|√|√|client & server
|
|VLESS |√| |√|√|client & server
|
||||||
|VMess | | |√|√|client only
|
|VMess | | |√|√|client only
|
||||||
|SSR | | |√| |client only
|
|SSR | | |√| |client only
|
||||||
|SSH | | |√| |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) {
|
func (s *AnyTLS) DialUDP(network, addr string) (net.PacketConn, error) {
|
||||||
|
if network != "udp" && network != "udp4" && network != "udp6" {
|
||||||
return nil, proxy.ErrNotSupported
|
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) {
|
func (s *AnyTLS) newClientSession() (*session, error) {
|
||||||
@ -105,3 +140,14 @@ func (c *clientConn) Close() error {
|
|||||||
_ = c.session.Close()
|
_ = c.session.Close()
|
||||||
return err
|
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"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/nadoo/glider/pkg/log"
|
"github.com/nadoo/glider/pkg/log"
|
||||||
"github.com/nadoo/glider/pkg/socks"
|
"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)
|
log.F("[anytls] read target error: %v", err)
|
||||||
return
|
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())
|
rc, dialer, err := s.proxy.Dial("tcp", target.String())
|
||||||
if err != nil {
|
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