Add SSH sniffer

This commit is contained in:
世界 2024-08-20 18:56:50 +08:00
parent b2c708a3e6
commit e39a28ed5a
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
6 changed files with 73 additions and 15 deletions

26
common/sniff/ssh.go Normal file
View file

@ -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
common/sniff/ssh_test.go Normal file
View file

@ -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)
}

View file

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

View file

@ -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` |

View file

@ -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 | `http` | Host | / |
| TCP | `tls` | Server Name | / | | TCP | `tls` | Server Name | / |
| UDP | `quic` | Server Name | QUIC 客户端类型 | | UDP | `quic` | Server Name | QUIC 客户端类型 |
| UDP | `stun` | / | / | | UDP | `stun` | / | / |
| TCP/UDP | `dns` | / | / | | TCP/UDP | `dns` | / | / |
| TCP/UDP | `bittorrent` | / | / | | TCP/UDP | `bittorrent` | / | / |
| UDP | `dtls` | / | / | | 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` |

View file

@ -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 {