Esta es una implementación de prueba de concepto de RTSP sobre el protocolo Dahua P2P. Funciona con Dahua y cámaras/NVR derivados.
El protocolo Dahua P2P se utiliza para el acceso remoto a dispositivos Dahua. Lo utilizan habitualmente aplicaciones Dahua como gDMSS Lite en Android o SmartPSS, KBiVMS en Windows.
En mi escenario específico, tengo un sistema CCTV KBVision. Aunque puedo acceder a las cámaras mediante el cliente KBiVMS, utilizo principalmente plataformas que no son Windows. Por lo tanto, quería explorar opciones alternativas para transmitir el vídeo utilizando un cliente RTSP, que tiene un soporte más amplio. Como resultado, decidí experimentar reimplementando el protocolo Dahua P2P.
src/*.rs
- Archivos fuente de RustCargo.toml
- Dependencias de Rustmain.py
- Script principalhelpers.py
- Funciones auxiliaresrequirements.txt
- Dependencias de Pythondh-p2p.lua
- Disector Wireshark para protocolo Dahua P2P Implementación de Rust utilizando programación asíncrona y patrón de paso de mensajes, lo que la hace más eficiente y flexible.
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
La implementación Python de DH-P2P es un enfoque simple y directo. Se utiliza con fines de redacción y prueba debido a su naturaleza rápida y fácil de escribir. Además, la implementación es más lineal y sigue un flujo de ejecución de arriba hacia abajo, lo que la hace más fácil de entender. Python, al ser un lenguaje de programación popular, contribuye aún más a su accesibilidad y familiaridad entre los desarrolladores.
# 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 "
Para usar el script con un dispositivo que requiere autenticación al crear un canal, use la opción -t 1
.
Cuando se ejecuta en modo --debug
o cuando --type
> 0, los argumentos USERNAME
y PASSWORD
son obligatorios. Además, asegúrese de que ffplay
esté en la ruta del sistema cuando el modo de depuración esté habilitado.
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
y -rtsp_transport tcp
Para realizar ingeniería inversa del protocolo, utilicé Wireshark y KBiVMS V2.02.0 como cliente en Windows. Con el disector dh-p2p.lua
, puede ver el protocolo en Wireshark más fácilmente.
Para el cliente RTSP, se puede utilizar VLC o ffplay para facilitar el control de las señales.
gráfico LR
Aplicación[[Este script]]
Servicio[Easy4IPCloud]
Dispositivo[Cámara/NVR]
Aplicación -- 1 --> Servicio
Servicio -- 2 --> Dispositivo
Aplicación <-. 3 .-> Dispositivo
El protocolo Dahua P2P se inicia con un protocolo de enlace P2P. Este proceso implica localizar el dispositivo utilizando su número de serie (SN) a través de un servicio de terceros, Easy4IPCloud:
gráfico LR
Dispositivo[Cámara/NVR]
Aplicación[[Este script]]
Cliente1[Cliente RTSP 1]
Cliente2[Cliente RTSP 2]
Clienten[Cliente RTSP n]
Cliente1 - TCP --> Aplicación
Cliente2 - TCP --> Aplicación
Clienten -- TCP --> Aplicación
Aplicación <-. UDPnProtocolo PTCP .-> Dispositivo
Después del protocolo de enlace P2P, el script comienza a escuchar conexiones RTSP en el puerto 554. Tras la conexión de un cliente, el script inicia un nuevo dominio dentro del protocolo PTCP. Básicamente, este script sirve como túnel entre el cliente y el dispositivo, facilitando la comunicación a través de la encapsulación PTCP.
diagrama de secuencia
participante A como este script
participante B como Easy4IPCloud
participante C1 como servidor P2P
participante C2 como servidor de retransmisión
participante C3 como Agente Servidor
participante D como cámara/NVR
A->>B: /sonda/p2psrv
B-->>A: ;
A->>B: /en línea/p2psrv/{SN}
B-->>A: información p2psrv
A->>C1: /sonda/dispositivo/{SN}
C1-->>A: ;
A->>B: /en línea/retransmisión
B-->>A: información del relé
A->>B: /dispositivo/{SN}/p2p-canal (*)
par
A->>C2: /relé/agente
C2-->>A: información del agente + token
A->>C3: /relé/inicio/{token}
C3-->>A: ;
fin
B-->>A: información del dispositivo
A->>B: /device/{SN}/relay-channel + información del agente
C3-->>A: ¡Información de red del servidor!
A->>C3: SINCRONIZACIÓN PTCP
A->>C3: signo de solicitud PTCP
C3-->>A: signo PTCP
A->>D: protocolo de enlace PTCP (*)
Nota : Ambas conexiones marcadas con (*)
y todas las conexiones posteriores al dispositivo deben utilizar el mismo puerto local UDP.
PTCP (PhonyTCP) es un protocolo propietario desarrollado por Dahua. Tiene el propósito de encapsular paquetes TCP dentro de paquetes UDP, permitiendo la creación de un túnel entre un cliente y un dispositivo detrás de un NAT.
Tenga en cuenta que la documentación oficial para PTCP no está disponible. La información proporcionada aquí se basa en ingeniería inversa.
El encabezado del paquete PTCP tiene una estructura fija de 24 bytes, como se describe a continuación:
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
: Un valor constante, PTCP
.sent
y recv
: realiza un seguimiento del número de bytes enviados y recibidos, respectivamente.pid
: la identificación del paquete.lmid
: la identificación local.rmid
: ID local del paquete recibido previamente.El cuerpo del paquete varía en tamaño (0, 4, 12 bytes o más) según el tipo de paquete. Su estructura es la siguiente:
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
: especifica el tipo de paquete.len
: La longitud del campo data
.realm
: El ID del reino de la conexión.padding
: bytes de relleno, siempre establecidos en 0.data
: Los datos del paquete.Tipos de paquetes:
0x00
: SYN, el cuerpo siempre tiene 4 bytes 0x00030100
.0x10
: datos TCP, donde len
es la longitud de los datos TCP.0x11
: Solicitud de puerto vinculante.0x12
: Estado de la conexión, donde los datos son CONN
o DISC
.realm
establecido en 0):0x13
: Latido del corazón, donde len
siempre es 0.0x17
0x18
0x19
: Autenticación.0x1a
: respuesta del servidor después de 0x19
.0x1b
: respuesta del cliente después de 0x1a
. Este proyecto ha sido inspirado e influenciado por los siguientes proyectos y personas: