From 3a351ee30173e87335f83a1a409a465653b13bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 7 Jul 2024 15:45:50 +0800 Subject: [PATCH] Introduce DTLS sniffer --- common/sniff/dtls.go | 31 ++++++++++++++++++++++++++++ common/sniff/dtls_test.go | 30 +++++++++++++++++++++++++++ constant/protocol.go | 1 + docs/configuration/route/sniff.md | 26 ++++++++++++++++------- docs/configuration/route/sniff.zh.md | 26 ++++++++++++++++------- route/router.go | 1 + 6 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 common/sniff/dtls.go create mode 100644 common/sniff/dtls_test.go diff --git a/common/sniff/dtls.go b/common/sniff/dtls.go new file mode 100644 index 00000000..6469b097 --- /dev/null +++ b/common/sniff/dtls.go @@ -0,0 +1,31 @@ +package sniff + +import ( + "context" + "os" + + "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" +) + +func DTLSRecord(ctx context.Context, packet []byte) (*adapter.InboundContext, error) { + const fixedHeaderSize = 13 + if len(packet) < fixedHeaderSize { + return nil, os.ErrInvalid + } + contentType := packet[0] + switch contentType { + case 20, 21, 22, 23, 25: + default: + return nil, os.ErrInvalid + } + versionMajor := packet[1] + if versionMajor != 0xfe { + return nil, os.ErrInvalid + } + versionMinor := packet[2] + if versionMinor != 0xff && versionMinor != 0xfd { + return nil, os.ErrInvalid + } + return &adapter.InboundContext{Protocol: C.ProtocolDTLS}, nil +} diff --git a/common/sniff/dtls_test.go b/common/sniff/dtls_test.go new file mode 100644 index 00000000..45f77126 --- /dev/null +++ b/common/sniff/dtls_test.go @@ -0,0 +1,30 @@ +package sniff_test + +import ( + "context" + "encoding/hex" + "testing" + + "github.com/sagernet/sing-box/common/sniff" + C "github.com/sagernet/sing-box/constant" + + "github.com/stretchr/testify/require" +) + +func TestSniffDTLSClientHello(t *testing.T) { + t.Parallel() + packet, err := hex.DecodeString("16fefd0000000000000000007e010000720000000000000072fefd668a43523798e064bd806d0c87660de9c611a59bbdfc3892c4e072d94f2cafc40000000cc02bc02fc00ac014c02cc0300100003c000d0010000e0403050306030401050106010807ff01000100000a00080006001d00170018000b00020100000e000900060008000700010000170000") + require.NoError(t, err) + metadata, err := sniff.DTLSRecord(context.Background(), packet) + require.NoError(t, err) + require.Equal(t, metadata.Protocol, C.ProtocolDTLS) +} + +func TestSniffDTLSClientApplicationData(t *testing.T) { + t.Parallel() + packet, err := hex.DecodeString("17fefd000100000000000100440001000000000001a4f682b77ecadd10f3f3a2f78d90566212366ff8209fd77314f5a49352f9bb9bd12f4daba0b4736ae29e46b9714d3b424b3e6d0234736619b5aa0d3f") + require.NoError(t, err) + metadata, err := sniff.DTLSRecord(context.Background(), packet) + require.NoError(t, err) + require.Equal(t, metadata.Protocol, C.ProtocolDTLS) +} diff --git a/constant/protocol.go b/constant/protocol.go index 2b7a9e0f..915e64ca 100644 --- a/constant/protocol.go +++ b/constant/protocol.go @@ -7,4 +7,5 @@ const ( ProtocolDNS = "dns" ProtocolSTUN = "stun" ProtocolBitTorrent = "bittorrent" + ProtocolDTLS = "dtls" ) diff --git a/docs/configuration/route/sniff.md b/docs/configuration/route/sniff.md index 7a3de02b..cc9a5c13 100644 --- a/docs/configuration/route/sniff.md +++ b/docs/configuration/route/sniff.md @@ -1,12 +1,22 @@ +--- +icon: material/new-box +--- + +!!! quote "Changes in sing-box 1.10.0" + + :material-plus: BitTorrent support + :material-plus: DTLS 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 | -|:-------:|:-----------:|:-----------:| -| TCP | HTTP | Host | -| TCP | TLS | Server Name | -| UDP | QUIC | Server Name | -| UDP | STUN | / | -| TCP/UDP | DNS | / | -| TCP/UDP | BitTorrent | / | \ No newline at end of file +| Network | Protocol | Domain Name | +|:-------:|:------------:|:-----------:| +| TCP | `http` | Host | +| TCP | `tls` | Server Name | +| UDP | `quic` | Server Name | +| UDP | `stun` | / | +| TCP/UDP | `dns` | / | +| TCP/UDP | `bittorrent` | / | +| UDP | `dtls` | / | diff --git a/docs/configuration/route/sniff.zh.md b/docs/configuration/route/sniff.zh.md index 553c6ed7..523ed44b 100644 --- a/docs/configuration/route/sniff.zh.md +++ b/docs/configuration/route/sniff.zh.md @@ -1,12 +1,22 @@ +--- +icon: material/new-box +--- + +!!! quote "sing-box 1.10.0 中的更改" + + :material-plus: BitTorrent 支持 + :material-plus: DTLS 支持 + 如果在入站中启用,则可以嗅探连接的协议和域名(如果存在)。 #### 支持的协议 -| 网络 | 协议 | 域名 | -|:-------:|:-----------:|:-----------:| -| TCP | HTTP | Host | -| TCP | TLS | Server Name | -| UDP | QUIC | Server Name | -| UDP | STUN | / | -| TCP/UDP | DNS | / | -| TCP/UDP | BitTorrent | / | \ No newline at end of file +| 网络 | 协议 | 域名 | +|:-------:|:------------:|:-----------:| +| TCP | `http` | Host | +| TCP | `tls` | Server Name | +| UDP | `quic` | Server Name | +| UDP | `stun` | / | +| TCP/UDP | `dns` | / | +| TCP/UDP | `bittorrent` | / | +| UDP | `dtls` | / | diff --git a/route/router.go b/route/router.go index bc068b6f..ef49941e 100644 --- a/route/router.go +++ b/route/router.go @@ -966,6 +966,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m sniff.STUNMessage, sniff.UTP, sniff.UDPTracker, + sniff.DTLSRecord, ) if sniffMetadata != nil { metadata.Protocol = sniffMetadata.Protocol