これは、Dahua P2P プロトコルを介した RTSP の実装の概念実証です。 Dahua および派生カメラ / NVR で動作します。
Dahua P2P プロトコルは、Dahua デバイスへのリモート アクセスに利用されます。これは、Android の gDMSS Lite または Windows の SmartPSS、KBiVMS などの Dahua アプリで一般的に使用されます。
私の特定のシナリオでは、KBVision CCTV システムを使用しています。 KBiVMS クライアントを使用してカメラにアクセスできますが、主に Windows 以外のプラットフォームを使用します。したがって、より広くサポートされている RTSP クライアントを使用してビデオをストリーミングするための代替オプションを検討したいと思いました。その結果、Dahua P2P プロトコルを再実装して実験することにしました。
src/*.rs
- RustのソースファイルCargo.toml
- Rust の依存関係main.py
- メインスクリプトhelpers.py
- ヘルパー関数requirements.txt
- Python の依存関係dh-p2p.lua
- Dahua P2P プロトコル用の Wireshark ディセクター非同期プログラミングとメッセージパッシングパターンを利用したRustの実装により、より効率的かつ柔軟になります。
A PoC implementation of TCP tunneling over Dahua P2P protocol.
Usage: dh-p2p [OPTIONS] <SERIAL>
Arguments:
<SERIAL> Serial number of the camera
Options:
-p, --port <[bind_address:]port:remote_port>
Bind address, port and remote port. Default: 127.0.0.1:1554:554
-h, --help
Print help
DH-P2P の Python 実装は、シンプルで直接的なアプローチです。素早く簡単に作成できるため、製図やテストの目的で使用されます。さらに、実装はより直線的でトップダウンの実行フローに従っており、理解しやすくなっています。 Python は人気のあるプログラミング言語であるため、開発者にとってのアクセシビリティと親しみやすさにさらに貢献しています。
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Run
python main.py [CAMERA_SERIAL]
# Stream (e.g. with ffplay) rtsp://[username]:[password]@127.0.0.1/cam/realmonitor?channel=1&subtype=0
ffplay -rtsp_transport tcp -i " rtsp://[username]:[password]@127.0.0.1/cam/realmonitor?channel=1&subtype=0 "
チャネルの作成時に認証が必要なデバイスでスクリプトを使用するには、 -t 1
オプションを使用します。
--debug
モードで実行する場合、または--type
> 0 の場合、 USERNAME
およびPASSWORD
引数は必須です。さらに、デバッグ モードが有効な場合は、 ffplay
がシステム パスにあることを確認してください。
usage: main.py [-h] [-u USERNAME] [-p PASSWORD] [-d] serial
positional arguments:
serial Serial number of the camera
options:
-h, --help show this help message and exit
-d, --debug Enable debug mode
-t TYPE, --type TYPE Type of the camera
-u USERNAME, --username USERNAME
Username of the camera
-p PASSWORD, --password PASSWORD
Password of the camer
ffplay
および-rtsp_transport tcp
オプションを使用するとより適切に動作しますプロトコルのリバース エンジニアリングのために、Windows 上のクライアントとして Wireshark と KBiVMS V2.02.0 を使用しました。 dh-p2p.lua
ディセクターを使用すると、Wireshark でプロトコルを簡単に確認できます。
RTSP クライアントの場合、信号を簡単に制御するために VLC または ffplay を使用できます。
グラフLR
アプリ[[このスクリプト]]
サービス[Easy4IPCloud]
デバイス[カメラ/NVR]
アプリ -- 1 --> サービス
サービス -- 2 --> デバイス
アプリ <-. 3 .-> デバイス
Dahua P2P プロトコルは、P2P ハンドシェイクで開始されます。このプロセスには、サードパーティ サービスである Easy4IPCloud 経由でシリアル番号 (SN) を使用してデバイスを特定することが含まれます。
グラフLR
デバイス[カメラ/NVR]
アプリ[[このスクリプト]]
クライアント1[RTSPクライアント1]
クライアント2[RTSPクライアント2]
クライアントn[RTSPクライアントn]
クライアント1 -- TCP --> アプリ
クライアント2 -- TCP --> アプリ
クライアント -- TCP --> アプリ
アプリ <-. UDPnPTCP プロトコル .-> デバイス
P2P ハンドシェイクに続いて、スクリプトはポート 554 で RTSP 接続の待機を開始します。クライアントが接続すると、スクリプトは PTCP プロトコル内で新しいレルムを開始します。基本的に、このスクリプトはクライアントとデバイス間のトンネルとして機能し、PTCP カプセル化による通信を容易にします。
シーケンス図
参加者 A がこのスクリプトとして
Easy4IPCloud としての参加者 B
P2P サーバーとしての参加者 C1
中継サーバーとしての参加者 C2
エージェントサーバーとしての参加者 C3
カメラ/NVRとしての参加者D
A->>B: /probe/p2psrv
B-->>A: ;
A->>B: /online/p2psrv/{SN}
B-->>A: p2psrv 情報
A->>C1: /プローブ/デバイス/{SN}
C1-->>A: ;
A->>B: /オンライン/リレー
B-->>A: リレー情報
A->>B: /device/{SN}/p2p-channel (*)
パー
A->>C2: /relay/agent
C2-->>A: エージェント情報 + トークン
A->>C3: /relay/start/{トークン}
C3-->>A: ;
終わり
B-->>A: デバイス情報
A->>B: /device/{SN}/リレーチャネル + エージェント情報
C3-->>A: サーバーのナット情報!
A->>C3: PTCP SYN
A->>C3: PTCP リクエストサイン
C3-->>A: PTCP サイン
A->>D: PTCP ハンドシェイク (*)
注: (*)
のマークが付いた両方の接続と、それ以降のデバイスへのすべての接続は、同じ UDP ローカル ポートを使用する必要があります。
PTCP (PhonyTCP) は、Dahua が開発した独自のプロトコルです。これは、TCP パケットを UDP パケット内にカプセル化する目的を果たし、クライアントと NAT の背後にあるデバイスとの間にトンネルを作成できるようにします。
PTCP の公式ドキュメントは入手できないことに注意してください。ここで提供される情報はリバース エンジニアリングに基づいています。
PTCP パケット ヘッダーは、以下に概要を示すように、固定の 24 バイト構造です。
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| magic |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| sent |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| recv |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| pid |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| lmid |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rmid |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
magic
: 定数値PTCP
。sent
とrecv
: それぞれ送信バイト数と受信バイト数を追跡します。pid
: パケットID。lmid
: ローカル ID。rmid
: 以前に受信したパケットのローカル ID。パケット本体のサイズは、パケット タイプに基づいて異なります (0、4、12 バイト以上)。その構造は次のとおりです。
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| type | len |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| realm |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type
: パケットのタイプを指定します。len
: data
フィールドの長さ。realm
: 接続のレルム ID。padding
: パディングバイト。常に 0 に設定されます。data
: パケットデータ。パケットの種類:
0x00
: SYN、本体は常に 4 バイト0x00030100
です。0x10
: TCP データ。len len
TCP データの長さです。0x11
: バインディングポートリクエスト。0x12
: 接続ステータス。データはCONN
またはDISC
のいずれかです。realm
を 0 に設定):0x13
: ハートビート、 len
は常に 0。0x17
0x18
0x19
: 認証。0x1a
: 0x19
以降のサーバー応答。0x1b
: 0x1a
の後のクライアント応答。 このプロジェクトは、次のプロジェクトや人々からインスピレーションを受け、影響を受けています。