From 68cdf18c99f4a64fe912bbb52e8557548cbfeb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 28 Aug 2024 14:22:19 +0800 Subject: [PATCH] Add RDP sniffer --- common/sniff/rdp.go | 90 ++++++++++++++++++++++++++++ common/sniff/rdp_test.go | 25 ++++++++ constant/protocol.go | 1 + docs/configuration/route/sniff.md | 8 ++- docs/configuration/route/sniff.zh.md | 26 ++++---- 5 files changed, 135 insertions(+), 15 deletions(-) create mode 100644 common/sniff/rdp.go create mode 100644 common/sniff/rdp_test.go diff --git a/common/sniff/rdp.go b/common/sniff/rdp.go new file mode 100644 index 00000000..391ebd26 --- /dev/null +++ b/common/sniff/rdp.go @@ -0,0 +1,90 @@ +package sniff + +import ( + "context" + "encoding/binary" + "io" + "os" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing/common/rw" +) + +func RDP(_ context.Context, metadata *adapter.InboundContext, reader io.Reader) error { + var tpktVersion uint8 + err := binary.Read(reader, binary.BigEndian, &tpktVersion) + if err != nil { + return err + } + if tpktVersion != 0x03 { + return os.ErrInvalid + } + + var tpktReserved uint8 + err = binary.Read(reader, binary.BigEndian, &tpktReserved) + if err != nil { + return err + } + if tpktReserved != 0x00 { + return os.ErrInvalid + } + + var tpktLength uint16 + err = binary.Read(reader, binary.BigEndian, &tpktLength) + if err != nil { + return err + } + + if tpktLength != 19 { + return os.ErrInvalid + } + + var cotpLength uint8 + err = binary.Read(reader, binary.BigEndian, &cotpLength) + if err != nil { + return err + } + + if cotpLength != 14 { + return os.ErrInvalid + } + + var cotpTpduType uint8 + err = binary.Read(reader, binary.BigEndian, &cotpTpduType) + if err != nil { + return err + } + if cotpTpduType != 0xE0 { + return os.ErrInvalid + } + + err = rw.SkipN(reader, 5) + if err != nil { + return err + } + + var rdpType uint8 + err = binary.Read(reader, binary.BigEndian, &rdpType) + if err != nil { + return err + } + if rdpType != 0x01 { + return os.ErrInvalid + } + var rdpFlags uint8 + err = binary.Read(reader, binary.BigEndian, &rdpFlags) + if err != nil { + return err + } + var rdpLength uint8 + err = binary.Read(reader, binary.BigEndian, &rdpLength) + if err != nil { + return err + } + if rdpLength != 8 { + return os.ErrInvalid + } + metadata.Protocol = C.ProtocolRDP + return nil +} diff --git a/common/sniff/rdp_test.go b/common/sniff/rdp_test.go new file mode 100644 index 00000000..06fa3ab2 --- /dev/null +++ b/common/sniff/rdp_test.go @@ -0,0 +1,25 @@ +package sniff_test + +import ( + "bytes" + "context" + "encoding/hex" + "testing" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/sniff" + C "github.com/sagernet/sing-box/constant" + + "github.com/stretchr/testify/require" +) + +func TestSniffRDP(t *testing.T) { + t.Parallel() + + pkt, err := hex.DecodeString("030000130ee00000000000010008000b000000010008000b000000") + require.NoError(t, err) + var metadata adapter.InboundContext + err = sniff.RDP(context.TODO(), &metadata, bytes.NewReader(pkt)) + require.NoError(t, err) + require.Equal(t, C.ProtocolRDP, metadata.Protocol) +} diff --git a/constant/protocol.go b/constant/protocol.go index f867f428..14854089 100644 --- a/constant/protocol.go +++ b/constant/protocol.go @@ -9,6 +9,7 @@ const ( ProtocolBitTorrent = "bittorrent" ProtocolDTLS = "dtls" ProtocolSSH = "ssh" + ProtocolRDP = "rdp" ) const ( diff --git a/docs/configuration/route/sniff.md b/docs/configuration/route/sniff.md index 70e2acc8..40de038c 100644 --- a/docs/configuration/route/sniff.md +++ b/docs/configuration/route/sniff.md @@ -8,14 +8,15 @@ icon: material/new-box :material-plus: Chromium support for QUIC :material-plus: BitTorrent support :material-plus: DTLS support - :material-plus: SSH support + :material-plus: SSH support + :material-plus: RDP support If enabled in the inbound, the protocol and domain name (if present) of by the connection can be sniffed. #### Supported Protocols | Network | Protocol | Domain Name | Client | -| :-----: | :----------: | :---------: | :--------------: | +|:-------:|:------------:|:-----------:|:----------------:| | TCP | `http` | Host | / | | TCP | `tls` | Server Name | / | | UDP | `quic` | Server Name | QUIC Client Type | @@ -24,9 +25,10 @@ If enabled in the inbound, the protocol and domain name (if present) of by the c | TCP/UDP | `bittorrent` | / | / | | UDP | `dtls` | / | / | | TCP | `ssh` | / | SSH Client Name | +| TCP | `rdp` | / | / | | QUIC Client | Type | -| :----------------------: | :--------: | +|:------------------------:|:----------:| | Chromium/Cronet | `chrimium` | | Safari/Apple Network API | `safari` | | Firefox / uquic firefox | `firefox` | diff --git a/docs/configuration/route/sniff.zh.md b/docs/configuration/route/sniff.zh.md index 7421fd07..4efa4538 100644 --- a/docs/configuration/route/sniff.zh.md +++ b/docs/configuration/route/sniff.zh.md @@ -8,25 +8,27 @@ icon: material/new-box :material-plus: QUIC 的 Chromium 支持 :material-plus: BitTorrent 支持 :material-plus: DTLS 支持 - :material-plus: SSH 支持 + :material-plus: SSH 支持 + :material-plus: RDP 支持 如果在入站中启用,则可以嗅探连接的协议和域名(如果存在)。 #### 支持的协议 -| 网络 | 协议 | 域名 | 客户端 | -| :-----: | :----------: | :---------: | :-------------: | -| TCP | `http` | Host | / | -| TCP | `tls` | Server Name | / | +| 网络 | 协议 | 域名 | 客户端 | +|:-------:|:------------:|:-----------:|:----------:| +| TCP | `http` | Host | / | +| TCP | `tls` | Server Name | / | | UDP | `quic` | Server Name | QUIC 客户端类型 | -| UDP | `stun` | / | / | -| TCP/UDP | `dns` | / | / | -| TCP/UDP | `bittorrent` | / | / | -| UDP | `dtls` | / | / | -| TCP | `SSH` | / | SSH 客户端名称 | +| UDP | `stun` | / | / | +| TCP/UDP | `dns` | / | / | +| TCP/UDP | `bittorrent` | / | / | +| UDP | `dtls` | / | / | +| TCP | `ssh` | / | SSH 客户端名称 | +| TCP | `rdp` | / | / | -| QUIC 客户端 | 类型 | -| :----------------------: | :--------: | +| QUIC 客户端 | 类型 | +|:------------------------:|:----------:| | Chromium/Cronet | `chrimium` | | Safari/Apple Network API | `safari` | | Firefox / uquic firefox | `firefox` |