mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-10-02 18:18:24 +00:00
* key exchange version 1 on the rendezvous and peer handshakes Bump hbb_common for key exchange version 1, one stream key per direction, and negotiate it on both handshakes. Rendezvous: `secure_tcp` reads the version the server advertises in `KeyExchange.version`, answers with the highest both speak, splits the key when that is at least 1, and arms the check of the server's echo on its first encrypted message. Peer: the controlled side advertises `KX_VERSION_LATEST` inside the signed `IdPk`, the controller answers in `PublicKey.kx_version` and both split the key when the pick is at least 1. A pick above what was offered is refused as a bug or tampering. `decode_id_pk_dtls` returns the advertised version along with the fingerprint. An absent field on either side is version 0, so any old peer or server keeps today's stream byte for byte. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns * bump hbb_common: keep the advertisement check armed until an echo arrives Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * bump hbb_common: drop box_pk_of until something calls it Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * key exchange: say 0 on WebRTC, where no stream key applies A WebRTC stream is encrypted by DTLS and `set_key_split` collapses to `set_key` on it, which sets nothing but `peer_verified`. Both sides still put 1 on the wire, the controlled peer in its signed identity and the controller in its pick, and agreed on a version neither applied. That held only because both fell into the same branch: the day one side splits keys on WebRTC and the other does not, they would agree on 1, derive different keys, and have no version mismatch to point at. The controlled peer now advertises 0 on WebRTC and refuses a pick above what it advertised rather than above the newest it speaks anywhere; the controller picks 0 there. What is on the wire is what runs. Also bumps hbb_common for the echo contract: the server tags every message after the exchange, not the first alone, so dropping one frame at a known position does not rid an attacker of the downgrade check. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab * key exchange: verify the server's signed version before trusting it bump hbb_common d9f519e: KeyExchange.signed_version A server that signs its version sets bit 255 of the ephemeral key inside the signed keys[0]. A client that sees that bit, or the field itself, verifies sign("rdkx-ver" || key || version LE) under the server's signing key before picking a version and refuses the exchange otherwise, so a version lowered in the clear is caught at the handshake rather than one frame pair later by the echo. A legacy server sends canonical keys and no field and takes the unchanged path; the echo still checks its version. Tests: version 1 keys match the server both ways; an advertisement lowered in transit is refused at the first echo; legacy, signed-1 and signed-3 servers each exchange application data; nine tamperings of the signed version are refused before the client replies. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns * key exchange: comments say what the fields and functions are bump hbb_common e3452ac: the same on the shared side. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns * key exchange: drop the advertisement echo bump hbb_common 5194159: RendezvousMessage.kx_advertised and the check on decrypted frames are gone. signed_version settles the version inside the exchange, so the client arms nothing after it; the stub in the tests stops tagging its frames and the test of a lowered advertisement goes, the signed-version cases covering that refusal before any reply. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns * key exchange: verify KxParams, the server's signed structure bump hbb_common e74a188: KeyExchange.signed_params and KxParams. The client parses the signed bytes as KxParams and compares its fields to the key it verified and the version it was shown, rather than matching a fixed byte string, so a field added to KxParams later parses past an older client. The tamper cases now include a payload signed as the old byte string. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns * key exchange: require the KxParams signing prefix before parsing bump hbb_common 26582e1: KX_PARAMS_DOMAIN. Without it a signed IdPk, which the server signs with the same key, parsed as a KxParams whose pk was the id and whose version was 0, so an id registered with the bytes of a marked ephemeral key could stand in for the params at version 0. Two tamper cases pin this: params signed without the prefix, and a signed IdPk in their place. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns * key exchange: one call sets the negotiated key bump hbb_common cbc8772: Stream::set_negotiated_key. The rendezvous client, the controller and the controlled side each branched on the picked version to choose between split and single keys. They now hand the transcript to Stream, which makes that choice once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns * Update hbb_common: refuse unknown key exchange versions, pin wire vectors Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns * client: prove the peer handshake picks its version and encrypts under it The rendezvous exchange had tests against a stub server; the peer handshake had none, so a controller that fell back to version 0 would still connect, still report secured, and pass every test. These run Client::secure_connection against a stub controlled peer and check the pick it sees and the application data both ways: new peers pick 1, a peer without versions gets 0, and a pick lowered in transit leaves the two sides unable to read each other. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns * Update hbb_common: pin the subkeys for an advertisement above the pick Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns * Update hbb_common to main, with #614 merged Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns * key exchange: name the picked version, say why the marker bit is safe Review follow-ups with no change in behaviour: the rendezvous pick is called picked, as in the peer handshake and KxTranscript; the marker comment says X25519 ignores the bit and the bytes stay as signed; the controlled side's refusal names the version it offered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
1027 lines
21 KiB
Protocol Buffer
1027 lines
21 KiB
Protocol Buffer
syntax = "proto3";
|
|
package hbb;
|
|
|
|
message EncodedVideoFrame {
|
|
bytes data = 1;
|
|
bool key = 2;
|
|
int64 pts = 3;
|
|
}
|
|
|
|
message EncodedVideoFrames { repeated EncodedVideoFrame frames = 1; }
|
|
|
|
message RGB { bool compress = 1; }
|
|
|
|
// planes data send directly in binary for better use arraybuffer on web
|
|
message YUV {
|
|
bool compress = 1;
|
|
int32 stride = 2;
|
|
}
|
|
|
|
enum Chroma {
|
|
I420 = 0;
|
|
I444 = 1;
|
|
}
|
|
|
|
message VideoFrame {
|
|
oneof union {
|
|
EncodedVideoFrames vp9s = 6;
|
|
RGB rgb = 7;
|
|
YUV yuv = 8;
|
|
EncodedVideoFrames h264s = 10;
|
|
EncodedVideoFrames h265s = 11;
|
|
EncodedVideoFrames vp8s = 12;
|
|
EncodedVideoFrames av1s = 13;
|
|
}
|
|
int32 display = 14;
|
|
}
|
|
|
|
message DisplayInfo {
|
|
sint32 x = 1;
|
|
sint32 y = 2;
|
|
int32 width = 3;
|
|
int32 height = 4;
|
|
string name = 5;
|
|
bool online = 6;
|
|
bool cursor_embedded = 7;
|
|
Resolution original_resolution = 8;
|
|
double scale = 9;
|
|
}
|
|
|
|
message PortForward {
|
|
string host = 1;
|
|
int32 port = 2;
|
|
bool multiplex = 3;
|
|
}
|
|
|
|
message FileTransfer {
|
|
string dir = 1;
|
|
bool show_hidden = 2;
|
|
}
|
|
|
|
message ViewCamera {}
|
|
|
|
message OSLogin {
|
|
string username = 1;
|
|
string password = 2;
|
|
}
|
|
|
|
message LoginRequest {
|
|
string username = 1;
|
|
bytes password = 2;
|
|
string my_id = 4;
|
|
string my_name = 5;
|
|
OptionMessage option = 6;
|
|
oneof union {
|
|
FileTransfer file_transfer = 7;
|
|
PortForward port_forward = 8;
|
|
ViewCamera view_camera = 15;
|
|
Terminal terminal = 16;
|
|
}
|
|
bool video_ack_required = 9;
|
|
uint64 session_id = 10;
|
|
string version = 11;
|
|
OSLogin os_login = 12;
|
|
string my_platform = 13;
|
|
bytes hwid = 14;
|
|
string avatar = 17;
|
|
}
|
|
|
|
message Terminal {
|
|
string service_id = 1; // Service ID for reconnecting to existing session
|
|
}
|
|
|
|
message Auth2FA {
|
|
string code = 1;
|
|
bytes hwid = 2;
|
|
}
|
|
|
|
message ChatMessage { string text = 1; }
|
|
|
|
message Features {
|
|
bool privacy_mode = 1;
|
|
bool terminal = 2;
|
|
bool port_forward_mux = 3;
|
|
}
|
|
|
|
message CodecAbility {
|
|
bool vp8 = 1;
|
|
bool vp9 = 2;
|
|
bool av1 = 3;
|
|
bool h264 = 4;
|
|
bool h265 = 5;
|
|
}
|
|
|
|
message SupportedEncoding {
|
|
bool h264 = 1;
|
|
bool h265 = 2;
|
|
bool vp8 = 3;
|
|
bool av1 = 4;
|
|
CodecAbility i444 = 5;
|
|
}
|
|
|
|
message PeerInfo {
|
|
string username = 1;
|
|
string hostname = 2;
|
|
string platform = 3;
|
|
repeated DisplayInfo displays = 4;
|
|
int32 current_display = 5;
|
|
bool sas_enabled = 6;
|
|
string version = 7;
|
|
Features features = 9;
|
|
SupportedEncoding encoding = 10;
|
|
SupportedResolutions resolutions = 11;
|
|
// Use JSON's key-value format which is friendly for peer to handle.
|
|
// NOTE: Only support one-level dictionaries (for peer to update), and the key is of type string.
|
|
string platform_additions = 12;
|
|
WindowsSessions windows_sessions = 13;
|
|
}
|
|
|
|
message WindowsSession {
|
|
uint32 sid = 1;
|
|
string name = 2;
|
|
}
|
|
|
|
message LoginResponse {
|
|
oneof union {
|
|
string error = 1;
|
|
PeerInfo peer_info = 2;
|
|
}
|
|
bool enable_trusted_devices = 3;
|
|
}
|
|
|
|
message TouchScaleUpdate {
|
|
// The delta scale factor relative to the previous scale.
|
|
// delta * 1000
|
|
// 0 means scale end
|
|
int32 scale = 1;
|
|
}
|
|
|
|
message TouchPanStart {
|
|
int32 x = 1;
|
|
int32 y = 2;
|
|
}
|
|
|
|
message TouchPanUpdate {
|
|
// The delta x position relative to the previous position.
|
|
int32 x = 1;
|
|
// The delta y position relative to the previous position.
|
|
int32 y = 2;
|
|
}
|
|
|
|
message TouchPanEnd {
|
|
int32 x = 1;
|
|
int32 y = 2;
|
|
}
|
|
|
|
message TouchEvent {
|
|
oneof union {
|
|
TouchScaleUpdate scale_update = 1;
|
|
TouchPanStart pan_start = 2;
|
|
TouchPanUpdate pan_update = 3;
|
|
TouchPanEnd pan_end = 4;
|
|
}
|
|
}
|
|
|
|
message PointerDeviceEvent {
|
|
oneof union {
|
|
TouchEvent touch_event = 1;
|
|
}
|
|
repeated ControlKey modifiers = 2;
|
|
}
|
|
|
|
message MouseEvent {
|
|
int32 mask = 1;
|
|
sint32 x = 2;
|
|
sint32 y = 3;
|
|
repeated ControlKey modifiers = 4;
|
|
}
|
|
|
|
enum KeyboardMode{
|
|
Legacy = 0;
|
|
Map = 1;
|
|
Translate = 2;
|
|
Auto = 3;
|
|
}
|
|
|
|
enum ControlKey {
|
|
Unknown = 0;
|
|
Alt = 1;
|
|
Backspace = 2;
|
|
CapsLock = 3;
|
|
Control = 4;
|
|
Delete = 5;
|
|
DownArrow = 6;
|
|
End = 7;
|
|
Escape = 8;
|
|
F1 = 9;
|
|
F10 = 10;
|
|
F11 = 11;
|
|
F12 = 12;
|
|
F2 = 13;
|
|
F3 = 14;
|
|
F4 = 15;
|
|
F5 = 16;
|
|
F6 = 17;
|
|
F7 = 18;
|
|
F8 = 19;
|
|
F9 = 20;
|
|
Home = 21;
|
|
LeftArrow = 22;
|
|
/// meta key (also known as "windows"; "super"; and "command")
|
|
Meta = 23;
|
|
/// option key on macOS (alt key on Linux and Windows)
|
|
Option = 24; // deprecated, use Alt instead
|
|
PageDown = 25;
|
|
PageUp = 26;
|
|
Return = 27;
|
|
RightArrow = 28;
|
|
Shift = 29;
|
|
Space = 30;
|
|
Tab = 31;
|
|
UpArrow = 32;
|
|
Numpad0 = 33;
|
|
Numpad1 = 34;
|
|
Numpad2 = 35;
|
|
Numpad3 = 36;
|
|
Numpad4 = 37;
|
|
Numpad5 = 38;
|
|
Numpad6 = 39;
|
|
Numpad7 = 40;
|
|
Numpad8 = 41;
|
|
Numpad9 = 42;
|
|
Cancel = 43;
|
|
Clear = 44;
|
|
Menu = 45; // deprecated, use Alt instead
|
|
Pause = 46;
|
|
Kana = 47;
|
|
Hangul = 48;
|
|
Junja = 49;
|
|
Final = 50;
|
|
Hanja = 51;
|
|
Kanji = 52;
|
|
Convert = 53;
|
|
Select = 54;
|
|
Print = 55;
|
|
Execute = 56;
|
|
Snapshot = 57;
|
|
Insert = 58;
|
|
Help = 59;
|
|
Sleep = 60;
|
|
Separator = 61;
|
|
Scroll = 62;
|
|
NumLock = 63;
|
|
RWin = 64;
|
|
Apps = 65;
|
|
Multiply = 66;
|
|
Add = 67;
|
|
Subtract = 68;
|
|
Decimal = 69;
|
|
Divide = 70;
|
|
Equals = 71;
|
|
NumpadEnter = 72;
|
|
RShift = 73;
|
|
RControl = 74;
|
|
RAlt = 75;
|
|
VolumeMute = 76; // mainly used on mobile devices as controlled side
|
|
VolumeUp = 77;
|
|
VolumeDown = 78;
|
|
Power = 79; // mainly used on mobile devices as controlled side
|
|
CtrlAltDel = 100;
|
|
LockScreen = 101;
|
|
}
|
|
|
|
message KeyEvent {
|
|
// `down` indicates the key's state(down or up).
|
|
bool down = 1;
|
|
// `press` indicates a click event(down and up).
|
|
bool press = 2;
|
|
oneof union {
|
|
ControlKey control_key = 3;
|
|
// position key code. win: scancode, linux: key code, macos: key code
|
|
uint32 chr = 4;
|
|
uint32 unicode = 5;
|
|
string seq = 6;
|
|
// high word. virtual keycode
|
|
// low word. unicode
|
|
uint32 win2win_hotkey = 7;
|
|
}
|
|
repeated ControlKey modifiers = 8;
|
|
KeyboardMode mode = 9;
|
|
}
|
|
|
|
message CursorData {
|
|
uint64 id = 1;
|
|
sint32 hotx = 2;
|
|
sint32 hoty = 3;
|
|
int32 width = 4;
|
|
int32 height = 5;
|
|
bytes colors = 6;
|
|
}
|
|
|
|
message CursorPosition {
|
|
sint32 x = 1;
|
|
sint32 y = 2;
|
|
}
|
|
|
|
message Hash {
|
|
string salt = 1;
|
|
string challenge = 2;
|
|
}
|
|
|
|
enum ClipboardFormat {
|
|
Text = 0;
|
|
Rtf = 1;
|
|
Html = 2;
|
|
ImageRgba = 21;
|
|
ImagePng = 22;
|
|
ImageSvg = 23;
|
|
Special = 31;
|
|
}
|
|
|
|
message Clipboard {
|
|
bool compress = 1;
|
|
bytes content = 2;
|
|
int32 width = 3;
|
|
int32 height = 4;
|
|
ClipboardFormat format = 5;
|
|
// Special format name, only used when format is Special.
|
|
string special_name = 6;
|
|
}
|
|
|
|
message MultiClipboards { repeated Clipboard clipboards = 1; }
|
|
|
|
enum FileType {
|
|
Dir = 0;
|
|
DirLink = 2;
|
|
DirDrive = 3;
|
|
File = 4;
|
|
FileLink = 5;
|
|
}
|
|
|
|
message FileEntry {
|
|
FileType entry_type = 1;
|
|
string name = 2;
|
|
bool is_hidden = 3;
|
|
uint64 size = 4;
|
|
uint64 modified_time = 5;
|
|
}
|
|
|
|
message FileDirectory {
|
|
int32 id = 1;
|
|
string path = 2;
|
|
repeated FileEntry entries = 3;
|
|
}
|
|
|
|
message ReadDir {
|
|
string path = 1;
|
|
bool include_hidden = 2;
|
|
}
|
|
|
|
message ReadEmptyDirs {
|
|
string path = 1;
|
|
bool include_hidden = 2;
|
|
}
|
|
|
|
message ReadEmptyDirsResponse {
|
|
string path = 1;
|
|
repeated FileDirectory empty_dirs = 2;
|
|
}
|
|
|
|
message ReadAllFiles {
|
|
int32 id = 1;
|
|
string path = 2;
|
|
bool include_hidden = 3;
|
|
}
|
|
|
|
message FileRename {
|
|
int32 id = 1;
|
|
string path = 2;
|
|
string new_name = 3;
|
|
}
|
|
|
|
message FileAction {
|
|
oneof union {
|
|
ReadDir read_dir = 1;
|
|
FileTransferSendRequest send = 2;
|
|
FileTransferReceiveRequest receive = 3;
|
|
FileDirCreate create = 4;
|
|
FileRemoveDir remove_dir = 5;
|
|
FileRemoveFile remove_file = 6;
|
|
ReadAllFiles all_files = 7;
|
|
FileTransferCancel cancel = 8;
|
|
FileTransferSendConfirmRequest send_confirm = 9;
|
|
FileRename rename = 10;
|
|
ReadEmptyDirs read_empty_dirs = 11;
|
|
}
|
|
}
|
|
|
|
message FileTransferCancel { int32 id = 1; }
|
|
|
|
message FileResponse {
|
|
oneof union {
|
|
FileDirectory dir = 1;
|
|
FileTransferBlock block = 2;
|
|
FileTransferError error = 3;
|
|
FileTransferDone done = 4;
|
|
FileTransferDigest digest = 5;
|
|
ReadEmptyDirsResponse empty_dirs = 6;
|
|
}
|
|
}
|
|
|
|
message FileTransferDigest {
|
|
int32 id = 1;
|
|
sint32 file_num = 2;
|
|
uint64 last_modified = 3;
|
|
uint64 file_size = 4;
|
|
bool is_upload = 5;
|
|
bool is_identical = 6;
|
|
uint64 transferred_size = 7; // For resume. Indicates the size of the file already transferred
|
|
bool is_resume = 8; // For resume. Indicates if the transfer is a resume.
|
|
// `is_resume` can let the controlled side know whether to check the `.digest` file.
|
|
// When `is_resume` is false, `.digest` exists, the same file does not exist,
|
|
// the controlled side should not check `.digest`, it should confirm with a new transfer request.
|
|
}
|
|
|
|
message FileTransferBlock {
|
|
int32 id = 1;
|
|
sint32 file_num = 2;
|
|
bytes data = 3;
|
|
bool compressed = 4;
|
|
uint32 blk_id = 5;
|
|
}
|
|
|
|
message FileTransferError {
|
|
int32 id = 1;
|
|
string error = 2;
|
|
sint32 file_num = 3;
|
|
}
|
|
|
|
message FileTransferSendRequest {
|
|
int32 id = 1;
|
|
string path = 2;
|
|
bool include_hidden = 3;
|
|
int32 file_num = 4;
|
|
|
|
enum FileType {
|
|
Generic = 0;
|
|
Printer = 1;
|
|
}
|
|
FileType file_type = 5;
|
|
}
|
|
|
|
message FileTransferSendConfirmRequest {
|
|
int32 id = 1;
|
|
sint32 file_num = 2;
|
|
oneof union {
|
|
bool skip = 3;
|
|
uint32 offset_blk = 4;
|
|
}
|
|
}
|
|
|
|
message FileTransferDone {
|
|
int32 id = 1;
|
|
sint32 file_num = 2;
|
|
}
|
|
|
|
message FileTransferReceiveRequest {
|
|
int32 id = 1;
|
|
string path = 2; // path written to
|
|
repeated FileEntry files = 3;
|
|
int32 file_num = 4;
|
|
uint64 total_size = 5;
|
|
}
|
|
|
|
message FileRemoveDir {
|
|
int32 id = 1;
|
|
string path = 2;
|
|
bool recursive = 3;
|
|
}
|
|
|
|
message FileRemoveFile {
|
|
int32 id = 1;
|
|
string path = 2;
|
|
sint32 file_num = 3;
|
|
}
|
|
|
|
message FileDirCreate {
|
|
int32 id = 1;
|
|
string path = 2;
|
|
}
|
|
|
|
// main logic from freeRDP
|
|
message CliprdrMonitorReady {
|
|
}
|
|
|
|
message CliprdrFormat {
|
|
int32 id = 2;
|
|
string format = 3;
|
|
}
|
|
|
|
message CliprdrServerFormatList {
|
|
repeated CliprdrFormat formats = 2;
|
|
}
|
|
|
|
message CliprdrServerFormatListResponse {
|
|
int32 msg_flags = 2;
|
|
}
|
|
|
|
message CliprdrServerFormatDataRequest {
|
|
int32 requested_format_id = 2;
|
|
}
|
|
|
|
message CliprdrServerFormatDataResponse {
|
|
int32 msg_flags = 2;
|
|
bytes format_data = 3;
|
|
}
|
|
|
|
message CliprdrFileContentsRequest {
|
|
int32 stream_id = 2;
|
|
int32 list_index = 3;
|
|
int32 dw_flags = 4;
|
|
int32 n_position_low = 5;
|
|
int32 n_position_high = 6;
|
|
int32 cb_requested = 7;
|
|
bool have_clip_data_id = 8;
|
|
int32 clip_data_id = 9;
|
|
}
|
|
|
|
message CliprdrFileContentsResponse {
|
|
int32 msg_flags = 3;
|
|
int32 stream_id = 4;
|
|
bytes requested_data = 5;
|
|
}
|
|
|
|
// Try empty clipboard in the following case(Windows only):
|
|
// 1. `A`(Windows) -> `B`, `C`
|
|
// 2. Copy in `A, file clipboards on `B` and `C` are updated.
|
|
// 3. Copy in `B`.
|
|
// `A` should tell `C` to empty the file clipboard.
|
|
message CliprdrTryEmpty {
|
|
}
|
|
|
|
// Clipobard file message for audit.
|
|
message CliprdrFile {
|
|
string name = 1;
|
|
uint64 size = 2;
|
|
}
|
|
|
|
message CliprdrFiles {
|
|
repeated CliprdrFile files = 1;
|
|
}
|
|
|
|
message Cliprdr {
|
|
oneof union {
|
|
CliprdrMonitorReady ready = 1;
|
|
CliprdrServerFormatList format_list = 2;
|
|
CliprdrServerFormatListResponse format_list_response = 3;
|
|
CliprdrServerFormatDataRequest format_data_request = 4;
|
|
CliprdrServerFormatDataResponse format_data_response = 5;
|
|
CliprdrFileContentsRequest file_contents_request = 6;
|
|
CliprdrFileContentsResponse file_contents_response = 7;
|
|
CliprdrTryEmpty try_empty = 8;
|
|
CliprdrFiles files = 9;
|
|
}
|
|
}
|
|
|
|
message Resolution {
|
|
int32 width = 1;
|
|
int32 height = 2;
|
|
}
|
|
|
|
message DisplayResolution {
|
|
int32 display = 1;
|
|
Resolution resolution = 2;
|
|
}
|
|
|
|
message SupportedResolutions { repeated Resolution resolutions = 1; }
|
|
|
|
message SwitchDisplay {
|
|
int32 display = 1;
|
|
sint32 x = 2;
|
|
sint32 y = 3;
|
|
int32 width = 4;
|
|
int32 height = 5;
|
|
bool cursor_embedded = 6;
|
|
SupportedResolutions resolutions = 7;
|
|
// Do not care about the origin point for now.
|
|
Resolution original_resolution = 8;
|
|
}
|
|
|
|
message CaptureDisplays {
|
|
repeated int32 add = 1;
|
|
repeated int32 sub = 2;
|
|
repeated int32 set = 3;
|
|
}
|
|
|
|
message ToggleVirtualDisplay {
|
|
int32 display = 1;
|
|
bool on = 2;
|
|
}
|
|
|
|
message TogglePrivacyMode {
|
|
string impl_key = 1;
|
|
bool on = 2;
|
|
}
|
|
|
|
message PermissionInfo {
|
|
enum Permission {
|
|
Keyboard = 0;
|
|
Clipboard = 2;
|
|
Audio = 3;
|
|
File = 4;
|
|
Restart = 5;
|
|
Recording = 6;
|
|
BlockInput = 7;
|
|
PrivacyMode = 8;
|
|
}
|
|
|
|
Permission permission = 1;
|
|
bool enabled = 2;
|
|
}
|
|
|
|
enum ImageQuality {
|
|
NotSet = 0;
|
|
Low = 2;
|
|
Balanced = 3;
|
|
Best = 4;
|
|
}
|
|
|
|
message SupportedDecoding {
|
|
enum PreferCodec {
|
|
Auto = 0;
|
|
VP9 = 1;
|
|
H264 = 2;
|
|
H265 = 3;
|
|
VP8 = 4;
|
|
AV1 = 5;
|
|
}
|
|
|
|
int32 ability_vp9 = 1;
|
|
int32 ability_h264 = 2;
|
|
int32 ability_h265 = 3;
|
|
PreferCodec prefer = 4;
|
|
int32 ability_vp8 = 5;
|
|
int32 ability_av1 = 6;
|
|
CodecAbility i444 = 7;
|
|
Chroma prefer_chroma = 8;
|
|
}
|
|
|
|
message OptionMessage {
|
|
enum BoolOption {
|
|
NotSet = 0;
|
|
No = 1;
|
|
Yes = 2;
|
|
}
|
|
ImageQuality image_quality = 1;
|
|
BoolOption lock_after_session_end = 2;
|
|
BoolOption show_remote_cursor = 3;
|
|
BoolOption privacy_mode = 4;
|
|
BoolOption block_input = 5;
|
|
int32 custom_image_quality = 6;
|
|
BoolOption disable_audio = 7;
|
|
BoolOption disable_clipboard = 8;
|
|
BoolOption enable_file_transfer = 9;
|
|
SupportedDecoding supported_decoding = 10;
|
|
int32 custom_fps = 11;
|
|
BoolOption disable_keyboard = 12;
|
|
// Position 13 is used for Resolution. Remove later.
|
|
// Resolution custom_resolution = 13;
|
|
// BoolOption support_windows_specific_session = 14;
|
|
// starting from 15 please, do not use removed fields
|
|
BoolOption follow_remote_cursor = 15;
|
|
BoolOption follow_remote_window = 16;
|
|
BoolOption disable_camera = 17;
|
|
BoolOption terminal_persistent = 18;
|
|
BoolOption show_my_cursor = 19;
|
|
}
|
|
|
|
message TestDelay {
|
|
int64 time = 1;
|
|
bool from_client = 2;
|
|
uint32 last_delay = 3;
|
|
uint32 target_bitrate = 4;
|
|
}
|
|
|
|
message PublicKey {
|
|
bytes asymmetric_value = 1;
|
|
bytes symmetric_value = 2;
|
|
// The key exchange version the controller picked, the highest both sides speak; absent,
|
|
// i.e. 0, is the original scheme.
|
|
uint32 kx_version = 3;
|
|
}
|
|
|
|
message SignedId { bytes id = 1; }
|
|
|
|
message AudioFormat {
|
|
uint32 sample_rate = 1;
|
|
uint32 channels = 2;
|
|
}
|
|
|
|
message AudioFrame {
|
|
bytes data = 1;
|
|
}
|
|
|
|
// Notify peer to show message box.
|
|
message MessageBox {
|
|
// Message type. Refer to flutter/lib/common.dart/msgBox().
|
|
string msgtype = 1;
|
|
string title = 2;
|
|
// English
|
|
string text = 3;
|
|
// If not empty, msgbox provides a button to following the link.
|
|
// The link here can't be directly http url.
|
|
// It must be the key of http url configed in peer side or "rustdesk://*" (jump in app).
|
|
string link = 4;
|
|
}
|
|
|
|
message BackNotification {
|
|
// no need to consider block input by someone else
|
|
enum BlockInputState {
|
|
BlkStateUnknown = 0;
|
|
BlkOnSucceeded = 2;
|
|
BlkOnFailed = 3;
|
|
BlkOffSucceeded = 4;
|
|
BlkOffFailed = 5;
|
|
}
|
|
enum PrivacyModeState {
|
|
PrvStateUnknown = 0;
|
|
// Privacy mode on by someone else
|
|
PrvOnByOther = 2;
|
|
// Privacy mode is not supported on the remote side
|
|
PrvNotSupported = 3;
|
|
// Privacy mode on by self
|
|
PrvOnSucceeded = 4;
|
|
// Privacy mode on by self, but denied
|
|
PrvOnFailedDenied = 5;
|
|
// Some plugins are not found
|
|
PrvOnFailedPlugin = 6;
|
|
// Privacy mode on by self, but failed
|
|
PrvOnFailed = 7;
|
|
// Privacy mode off by self
|
|
PrvOffSucceeded = 8;
|
|
// Ctrl + P
|
|
PrvOffByPeer = 9;
|
|
// Privacy mode off by self, but failed
|
|
PrvOffFailed = 10;
|
|
PrvOffUnknown = 11;
|
|
}
|
|
|
|
oneof union {
|
|
PrivacyModeState privacy_mode_state = 1;
|
|
BlockInputState block_input_state = 2;
|
|
}
|
|
// Supplementary message, for "PrvOnFailed" and "PrvOffFailed"
|
|
string details = 3;
|
|
// The key of the implementation
|
|
string impl_key = 4;
|
|
}
|
|
|
|
message ElevationRequestWithLogon {
|
|
string username = 1;
|
|
string password = 2;
|
|
}
|
|
|
|
message ElevationRequest {
|
|
oneof union {
|
|
bool direct = 1;
|
|
ElevationRequestWithLogon logon = 2;
|
|
}
|
|
}
|
|
|
|
message SwitchSidesRequest {
|
|
bytes uuid = 1;
|
|
}
|
|
|
|
message SwitchSidesResponse {
|
|
bytes uuid = 1;
|
|
LoginRequest lr = 2;
|
|
}
|
|
|
|
message SwitchBack {}
|
|
|
|
message PluginRequest {
|
|
string id = 1;
|
|
bytes content = 2;
|
|
}
|
|
|
|
message PluginFailure {
|
|
string id = 1;
|
|
string name = 2;
|
|
string msg = 3;
|
|
}
|
|
|
|
message WindowsSessions {
|
|
repeated WindowsSession sessions = 1;
|
|
uint32 current_sid = 2;
|
|
}
|
|
|
|
// Query messages from peer.
|
|
message MessageQuery {
|
|
// The SwitchDisplay message of the target display.
|
|
// If the target display is not found, the message will be ignored.
|
|
int32 switch_display = 1;
|
|
}
|
|
|
|
message Misc {
|
|
oneof union {
|
|
ChatMessage chat_message = 4;
|
|
SwitchDisplay switch_display = 5;
|
|
PermissionInfo permission_info = 6;
|
|
OptionMessage option = 7;
|
|
AudioFormat audio_format = 8;
|
|
string close_reason = 9;
|
|
bool refresh_video = 10;
|
|
bool video_received = 12;
|
|
BackNotification back_notification = 13;
|
|
bool restart_remote_device = 14;
|
|
bool uac = 15;
|
|
bool foreground_window_elevated = 16;
|
|
bool stop_service = 17;
|
|
ElevationRequest elevation_request = 18;
|
|
string elevation_response = 19;
|
|
bool portable_service_running = 20;
|
|
SwitchSidesRequest switch_sides_request = 21;
|
|
SwitchBack switch_back = 22;
|
|
// Deprecated since 1.2.4, use `change_display_resolution` (36) instead.
|
|
// But we must keep it for compatibility when peer version < 1.2.4.
|
|
Resolution change_resolution = 24;
|
|
PluginRequest plugin_request = 25;
|
|
PluginFailure plugin_failure = 26;
|
|
uint32 full_speed_fps = 27; // deprecated
|
|
uint32 auto_adjust_fps = 28;
|
|
bool client_record_status = 29;
|
|
CaptureDisplays capture_displays = 30;
|
|
int32 refresh_video_display = 31;
|
|
ToggleVirtualDisplay toggle_virtual_display = 32;
|
|
TogglePrivacyMode toggle_privacy_mode = 33;
|
|
SupportedEncoding supported_encoding = 34;
|
|
uint32 selected_sid = 35;
|
|
DisplayResolution change_display_resolution = 36;
|
|
MessageQuery message_query = 37;
|
|
int32 follow_current_display = 38;
|
|
}
|
|
}
|
|
|
|
message VoiceCallRequest {
|
|
int64 req_timestamp = 1;
|
|
// Indicates whether the request is a connect action or a disconnect action.
|
|
bool is_connect = 2;
|
|
}
|
|
|
|
message VoiceCallResponse {
|
|
bool accepted = 1;
|
|
int64 req_timestamp = 2; // Should copy from [VoiceCallRequest::req_timestamp].
|
|
int64 ack_timestamp = 3;
|
|
}
|
|
|
|
message ScreenshotRequest {
|
|
int32 display = 1;
|
|
// sid is the session id on the controlling side
|
|
// It is used to forward the message to the correct remote (session) window.
|
|
string sid = 2;
|
|
}
|
|
|
|
message ScreenshotResponse {
|
|
string sid = 1;
|
|
// empty if success
|
|
string msg = 2;
|
|
bytes data = 3;
|
|
}
|
|
|
|
// Terminal messages - standalone feature like FileAction
|
|
message OpenTerminal {
|
|
int32 terminal_id = 1; // 0 for default terminal
|
|
uint32 rows = 2;
|
|
uint32 cols = 3;
|
|
}
|
|
|
|
message ResizeTerminal {
|
|
int32 terminal_id = 1;
|
|
uint32 rows = 2;
|
|
uint32 cols = 3;
|
|
}
|
|
|
|
message TerminalData {
|
|
int32 terminal_id = 1;
|
|
bytes data = 2;
|
|
bool compressed = 3;
|
|
}
|
|
|
|
message CloseTerminal {
|
|
int32 terminal_id = 1;
|
|
}
|
|
|
|
message TerminalAction {
|
|
oneof union {
|
|
OpenTerminal open = 1;
|
|
TerminalData data = 2;
|
|
ResizeTerminal resize = 3;
|
|
CloseTerminal close = 4;
|
|
}
|
|
}
|
|
|
|
message TerminalOpened {
|
|
int32 terminal_id = 1;
|
|
bool success = 2;
|
|
string message = 3;
|
|
uint32 pid = 4;
|
|
string service_id = 5; // Service ID for persistent sessions
|
|
repeated int32 persistent_sessions = 6; // Used to restore the persistent sessions.
|
|
bool replay_terminal_output = 7; // Whether the next data response replays buffered terminal output.
|
|
}
|
|
|
|
message TerminalClosed {
|
|
int32 terminal_id = 1;
|
|
int32 exit_code = 2;
|
|
}
|
|
|
|
message TerminalError {
|
|
int32 terminal_id = 1;
|
|
string message = 2;
|
|
}
|
|
|
|
message TerminalResponse {
|
|
oneof union {
|
|
TerminalOpened opened = 1;
|
|
TerminalData data = 2;
|
|
TerminalClosed closed = 3;
|
|
TerminalError error = 4;
|
|
}
|
|
}
|
|
|
|
message PortForwardOpen {
|
|
int32 channel_id = 1;
|
|
string host = 2;
|
|
int32 port = 3;
|
|
uint32 window = 4;
|
|
}
|
|
|
|
message PortForwardOpened {
|
|
int32 channel_id = 1;
|
|
bool success = 2;
|
|
string message = 3;
|
|
uint32 window = 4;
|
|
}
|
|
|
|
message PortForwardData {
|
|
int32 channel_id = 1;
|
|
bytes data = 2;
|
|
}
|
|
|
|
message PortForwardClose {
|
|
int32 channel_id = 1;
|
|
}
|
|
|
|
message PortForwardWindowUpdate {
|
|
int32 channel_id = 1;
|
|
uint32 add = 2;
|
|
}
|
|
|
|
// One symmetric message for both directions. `open` only travels controller -> controlled,
|
|
// `opened` only controlled -> controller; a frame in the wrong direction is ignored.
|
|
message PortForwardChannel {
|
|
oneof union {
|
|
PortForwardOpen open = 1;
|
|
PortForwardOpened opened = 2;
|
|
PortForwardData data = 3;
|
|
PortForwardClose close = 4;
|
|
PortForwardWindowUpdate window_update = 5;
|
|
}
|
|
}
|
|
|
|
message Message {
|
|
oneof union {
|
|
SignedId signed_id = 3;
|
|
PublicKey public_key = 4;
|
|
TestDelay test_delay = 5;
|
|
VideoFrame video_frame = 6;
|
|
LoginRequest login_request = 7;
|
|
LoginResponse login_response = 8;
|
|
Hash hash = 9;
|
|
MouseEvent mouse_event = 10;
|
|
AudioFrame audio_frame = 11;
|
|
CursorData cursor_data = 12;
|
|
CursorPosition cursor_position = 13;
|
|
uint64 cursor_id = 14;
|
|
KeyEvent key_event = 15;
|
|
Clipboard clipboard = 16;
|
|
FileAction file_action = 17;
|
|
FileResponse file_response = 18;
|
|
Misc misc = 19;
|
|
Cliprdr cliprdr = 20;
|
|
MessageBox message_box = 21;
|
|
SwitchSidesResponse switch_sides_response = 22;
|
|
VoiceCallRequest voice_call_request = 23;
|
|
VoiceCallResponse voice_call_response = 24;
|
|
PeerInfo peer_info = 25;
|
|
PointerDeviceEvent pointer_device_event = 26;
|
|
Auth2FA auth_2fa = 27;
|
|
MultiClipboards multi_clipboards = 28;
|
|
ScreenshotRequest screenshot_request = 29;
|
|
ScreenshotResponse screenshot_response= 30;
|
|
TerminalAction terminal_action = 31;
|
|
TerminalResponse terminal_response = 32;
|
|
PortForwardChannel port_forward_channel = 33;
|
|
}
|
|
}
|