浏览代码

Add SSH sniffer

世界 1 年之前
父节点
当前提交
04613c27e2
共有 6 个文件被更改,包括 73 次插入15 次删除
  1. 26 0
      common/sniff/ssh.go
  2. 26 0
      common/sniff/ssh_test.go
  3. 1 0
      constant/protocol.go
  4. 5 3
      docs/configuration/route/sniff.md
  5. 13 11
      docs/configuration/route/sniff.zh.md
  6. 2 1
      route/router.go

+ 26 - 0
common/sniff/ssh.go

@@ -0,0 +1,26 @@
+package sniff
+
+import (
+	"bufio"
+	"context"
+	"io"
+	"os"
+	"strings"
+
+	"github.com/sagernet/sing-box/adapter"
+	C "github.com/sagernet/sing-box/constant"
+)
+
+func SSH(_ context.Context, metadata *adapter.InboundContext, reader io.Reader) error {
+	scanner := bufio.NewScanner(reader)
+	if !scanner.Scan() {
+		return os.ErrInvalid
+	}
+	fistLine := scanner.Text()
+	if !strings.HasPrefix(fistLine, "SSH-2.0-") {
+		return os.ErrInvalid
+	}
+	metadata.Protocol = C.ProtocolSSH
+	metadata.Client = fistLine[8:]
+	return nil
+}

+ 26 - 0
common/sniff/ssh_test.go

@@ -0,0 +1,26 @@
+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 TestSniffSSH(t *testing.T) {
+	t.Parallel()
+
+	pkt, err := hex.DecodeString("5353482d322e302d64726f70626561720d0a000001a40a1492892570d1223aef61b0d647972c8bd30000009f637572766532353531392d7368613235362c637572766532353531392d736861323536406c69627373682e6f72672c6469666669652d68656c6c6d616e2d67726f757031342d7368613235362c6469666669652d68656c6c6d616e2d67726f757031342d736861312c6b6578677565737332406d6174742e7563632e61736e2e61752c6b65782d7374726963742d732d763030406f70656e7373682e636f6d000000207373682d656432353531392c7273612d736861322d3235362c7373682d7273610000003363686163686132302d706f6c7931333035406f70656e7373682e636f6d2c6165733132382d6374722c6165733235362d6374720000003363686163686132302d706f6c7931333035406f70656e7373682e636f6d2c6165733132382d6374722c6165733235362d63747200000017686d61632d736861312c686d61632d736861322d32353600000017686d61632d736861312c686d61632d736861322d323536000000046e6f6e65000000046e6f6e65000000000000000000000000002aa6ed090585b7d635b6")
+	require.NoError(t, err)
+	var metadata adapter.InboundContext
+	err = sniff.SSH(context.TODO(), &metadata, bytes.NewReader(pkt))
+	require.NoError(t, err)
+	require.Equal(t, C.ProtocolSSH, metadata.Protocol)
+	require.Equal(t, "dropbear", metadata.Client)
+}

+ 1 - 0
constant/protocol.go

@@ -8,6 +8,7 @@ const (
 	ProtocolSTUN       = "stun"
 	ProtocolSTUN       = "stun"
 	ProtocolBitTorrent = "bittorrent"
 	ProtocolBitTorrent = "bittorrent"
 	ProtocolDTLS       = "dtls"
 	ProtocolDTLS       = "dtls"
+	ProtocolSSH        = "ssh"
 )
 )
 
 
 const (
 const (

+ 5 - 3
docs/configuration/route/sniff.md

@@ -7,14 +7,15 @@ icon: material/new-box
     :material-plus: QUIC client type detect support for QUIC  
     :material-plus: QUIC client type detect support for QUIC  
     :material-plus: Chromium support for QUIC  
     :material-plus: Chromium support for QUIC  
     :material-plus: BitTorrent support  
     :material-plus: BitTorrent support  
-    :material-plus: DTLS support
+    :material-plus: DTLS support  
+    :material-plus: SSH support
 
 
 If enabled in the inbound, the protocol and domain name (if present) of by the connection can be sniffed.
 If enabled in the inbound, the protocol and domain name (if present) of by the connection can be sniffed.
 
 
 #### Supported Protocols
 #### Supported Protocols
 
 
 | Network |   Protocol   | Domain Name |      Client      |
 | Network |   Protocol   | Domain Name |      Client      |
-|:-------:|:------------:|:-----------:|:----------------:|
+| :-----: | :----------: | :---------: | :--------------: |
 |   TCP   |    `http`    |    Host     |        /         |
 |   TCP   |    `http`    |    Host     |        /         |
 |   TCP   |    `tls`     | Server Name |        /         |
 |   TCP   |    `tls`     | Server Name |        /         |
 |   UDP   |    `quic`    | Server Name | QUIC Client Type |
 |   UDP   |    `quic`    | Server Name | QUIC Client Type |
@@ -22,9 +23,10 @@ If enabled in the inbound, the protocol and domain name (if present) of by the c
 | TCP/UDP |    `dns`     |      /      |        /         |
 | TCP/UDP |    `dns`     |      /      |        /         |
 | TCP/UDP | `bittorrent` |      /      |        /         |
 | TCP/UDP | `bittorrent` |      /      |        /         |
 |   UDP   |    `dtls`    |      /      |        /         |
 |   UDP   |    `dtls`    |      /      |        /         |
+|   TCP   |    `ssh`     |      /      | SSH Client Name  |
 
 
 |       QUIC Client        |    Type    |
 |       QUIC Client        |    Type    |
-|:------------------------:|:----------:|
+| :----------------------: | :--------: |
 |     Chromium/Cronet      | `chrimium` |
 |     Chromium/Cronet      | `chrimium` |
 | Safari/Apple Network API |  `safari`  |
 | Safari/Apple Network API |  `safari`  |
 | Firefox / uquic firefox  | `firefox`  |
 | Firefox / uquic firefox  | `firefox`  |

+ 13 - 11
docs/configuration/route/sniff.zh.md

@@ -7,24 +7,26 @@ icon: material/new-box
     :material-plus: QUIC 的 客户端类型探测支持  
     :material-plus: QUIC 的 客户端类型探测支持  
     :material-plus: QUIC 的 Chromium 支持  
     :material-plus: QUIC 的 Chromium 支持  
     :material-plus: BitTorrent 支持  
     :material-plus: BitTorrent 支持  
-    :material-plus: DTLS 支持
+    :material-plus: DTLS 支持  
+    :material-plus: SSH 支持
 
 
 如果在入站中启用,则可以嗅探连接的协议和域名(如果存在)。
 如果在入站中启用,则可以嗅探连接的协议和域名(如果存在)。
 
 
 #### 支持的协议
 #### 支持的协议
 
 
-|   网络    |      协议      |     域名      |    客户端     |
-|:-------:|:------------:|:-----------:|:----------:|
-|   TCP   |    `http`    |    Host     |     /      |
-|   TCP   |    `tls`     | Server Name |     /      |
+|  网络   |     协议     |    域名     |     客户端      |
+| :-----: | :----------: | :---------: | :-------------: |
+|   TCP   |    `http`    |    Host     |        /        |
+|   TCP   |    `tls`     | Server Name |        /        |
 |   UDP   |    `quic`    | Server Name | QUIC 客户端类型 |
 |   UDP   |    `quic`    | Server Name | QUIC 客户端类型 |
-|   UDP   |    `stun`    |      /      |     /      |
-| TCP/UDP |    `dns`     |      /      |     /      |
-| TCP/UDP | `bittorrent` |      /      |     /      |
-|   UDP   |    `dtls`    |      /      |     /      |
+|   UDP   |    `stun`    |      /      |        /        |
+| TCP/UDP |    `dns`     |      /      |        /        |
+| TCP/UDP | `bittorrent` |      /      |        /        |
+|   UDP   |    `dtls`    |      /      |        /        |
+|   TCP   |    `SSH`     |      /      | SSH 客户端名称  |
 
 
-|         QUIC 客户端         |     类型     |
-|:------------------------:|:----------:|
+|       QUIC 客户端        |    类型    |
+| :----------------------: | :--------: |
 |     Chromium/Cronet      | `chrimium` |
 |     Chromium/Cronet      | `chrimium` |
 | Safari/Apple Network API |  `safari`  |
 | Safari/Apple Network API |  `safari`  |
 | Firefox / uquic firefox  | `firefox`  |
 | Firefox / uquic firefox  | `firefox`  |

+ 2 - 1
route/router.go

@@ -860,9 +860,10 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
 			conn,
 			conn,
 			buffer,
 			buffer,
 			time.Duration(metadata.InboundOptions.SniffTimeout),
 			time.Duration(metadata.InboundOptions.SniffTimeout),
-			sniff.StreamDomainNameQuery,
 			sniff.TLSClientHello,
 			sniff.TLSClientHello,
 			sniff.HTTPHost,
 			sniff.HTTPHost,
+			sniff.StreamDomainNameQuery,
+			sniff.SSH,
 			sniff.BitTorrent,
 			sniff.BitTorrent,
 		)
 		)
 		if err == nil {
 		if err == nil {