Skip to content

Commit e64e589

Browse files
authored
Merge pull request #9 from davehorner/jetkvm_control_svr_initial_release
Jetkvm control svr initial release
2 parents d357816 + 48b92c8 commit e64e589

17 files changed

+410
-49
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.1.4](https://github.com/davehorner/jetkvm_control/compare/jetkvm_control-v0.1.3...jetkvm_control-v0.1.4) - 2025-03-09
11+
12+
### Added
13+
14+
- initial release of jetkvm_control_svr with TLS and HMAC authentication
15+
- *(keyboard)* add send_key_combinations API and Lua binding
16+
- *(scripts)* add scripts folder with examples on automating the server and client.
17+
- *(examples)* add windows-alt-tab.lua, windows-notepad-helloworld.lua, windows-is_cmd_running.lua
18+
- *(doc)* windows-alt-tab.lua has been extensively documented, specifically for send_key_combinations
19+
20+
### Fixed
21+
22+
- *(jetkvm_control_svr)* enhance cryptographic provider setup
23+
1024
## [0.1.3](https://github.com/davehorner/jetkvm_control/compare/v0.1.2...v0.1.3) - 2025-03-03
1125

1226
### Other

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description = "A control client for JetKVM over WebRTC."
44
license = "MIT"
55
repository = "https://github.com/davehorner/jetkvm_control"
66
homepage = "https://github.com/davehorner/jetkvm_control"
7-
version = "0.1.3"
7+
version = "0.1.4"
88
edition = "2021"
99
authors = ["David Horner"]
1010

README.md

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,19 @@
11
# jetkvm_control
22

3-
**jetkvm_control** is a Rust library and client for interacting with JetKVM devices using WebRTC and JSON‑RPC. It provides functionality to authenticate with a JetKVM server, set up a WebRTC PeerConnection with a DataChannel, and send various input events (keyboard and mouse) as well as receive notifications (such as screen resolution updates) from the device.
3+
**jetkvm_control** is a Rust client/server/library for interacting with JetKVM devices using WebRTC and JSON‑RPC. It provides functionality to authenticate with a JetKVM device, set up a WebRTC PeerConnection with a DataChannel, and send various input events (keyboard and mouse) as well as receive notifications (such as screen resolution updates) from the device.
4+
5+
6+
> **Note:** Starting with version **0.1.4 (2025-03-09)**, a new secure RPC server (**jetkvm_control_svr**) has been introduced. This server supports TLS encryption and HMAC authentication, and includes features such as active_window and active_process interrogation. The server is cross-platform. The `jetkvm_control_svr` may be used without a JetKVM device. It comes with a rust client and Lua bindings.
7+
48

59
## Features
610

7-
- **HTTP Authentication:** Log in to a JetKVM server with cookie-based authentication.
8-
- **WebRTC Connection:** Establish a WebRTC PeerConnection and DataChannel.
9-
- **JSON‑RPC Messaging:** Send JSON‑RPC calls over the DataChannel for various commands.
1011
- **Keyboard Input:** Functions for sending keyboard events including text, control combinations (Ctrl-A, Ctrl-C, Ctrl-V, Ctrl-X, etc.), and special keys (Return, Windows key, etc.).
1112
- **Mouse Control:** Functions for absolute mouse movement, clicks (left, right, middle), double-click, and click-and-drag actions.
12-
- **Notification Handling:** Receive notifications (e.g. videoInputState) and update internal state (such as screen resolution).
13-
- **Configurable:** Easily configure connection parameters (IP, port, API endpoint, and password) using environment variables. For a simple parameter bag, use the tuple-struct `JetKvmParams`.
13+
- **jetkvm_control_svr:** to monitor processes and windows during script execution.
1414

1515
## Installation
1616

17-
Add this crate as a dependency in your `Cargo.toml`:
18-
19-
```toml
20-
[dependencies]
21-
jetkvm_control = "0.1.3" # or use a git dependency / local path during development
22-
```
23-
24-
### Setup
25-
2617
1. **Install via cargo**
2718
```
2819
cargo install jetkvm_control
@@ -36,22 +27,19 @@ jetkvm_control = "0.1.3" # or use a git dependency / local path during developm
3627
cd jetkvm_control
3728
```
3829
39-
3. **Configure Your Settings**
30+
2. **Configure Your Settings**
4031
41-
Before running the project, update your configuration settings. The project reads its configuration from either a config.toml file or environment variables. For example, create a config.toml with your settings:
32+
The project reads its configuration from either a config.toml file:
4233
```toml
4334
host = "host/ip"
4435
password = "your_password_here"
4536
port = "80"
4637
api = "/webrtc/session"
4738
```
4839
49-
You can also override these values via the command-line. For example:
50-
```
51-
cargo run -- --host 192.168.1.100 --port 8080
52-
```
40+
You can also override these values via the command-line but defining it in the config.toml makes the example scripts work.
5341
54-
4. **Running the Project**
42+
3. **Running the Project**
5543
After setting up your configuration, you can build and run the project with Cargo:
5644
```bash
5745
cargo run -- -H 192.168.1.100 lua-examples/windows-notepad-helloworld.lua
@@ -85,7 +73,7 @@ Options:
8573
8674
## What's the code look like
8775
88-
The api is subject to change.
76+
The api is subject to change. This project adheres to the "Semantic Versioning" standard.
8977
9078
example code for rust:
9179
```rust
@@ -146,19 +134,18 @@ Check out the examples folder for additional detail.
146134
## **Configuration Loading Precedence**
147135
The configuration file (`config.toml`) is loaded based on the following priority order:
148136

149-
### **📌 Priority Order**
137+
### ** Priority Order**
150138
| Priority | macOS/Linux | Windows |
151139
|----------|------------------------------|------------------------------------------|
152-
| 1️⃣ **(Highest)** | `config.toml` (Current Directory) | `config.toml` (Current Directory) |
153-
| 2️⃣ | `${CARGO_MANIFEST_DIR}/config.toml` | `${CARGO_MANIFEST_DIR}/config.toml` |
154-
| 3️⃣ **(System-Wide)** | `/etc/jetkvm_control/config.toml` | `%APPDATA%\jetkvm_control\config.toml` |
140+
| 1️⃣**(Highest)** | `config.toml` (Current Directory) | `config.toml` (Current Directory) |
141+
| 2️⃣| `${CARGO_MANIFEST_DIR}/config.toml` | `${CARGO_MANIFEST_DIR}/config.toml` |
142+
| 3️⃣**(System-Wide)** | `/etc/jetkvm_control/config.toml` | `%APPDATA%\jetkvm_control\config.toml` |
155143

156144
### **📍 How Configuration is Resolved**
157145
- **Current Directory (`config.toml`)** – Preferred for local development.
158146
- **Cargo Project Root (`CARGO_MANIFEST_DIR/config.toml`)** – Used when running inside a Rust project.
159147
- **System-Wide Location (`/etc/jetkvm_control/config.toml` or `%APPDATA%\jetkvm_control\config.toml`)** – Used when no local config is found.
160148

161-
If no configuration file is found, the program exits with an error message.
162149

163150
## Note
164151
- Password-less and Password-based local authentication have been tested functional.

jetkvm_control_platform/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
[package]
22
name = "jetkvm_control_platform"
3-
version = "0.1.0"
43
edition = "2021"
54

65
[dependencies]

jetkvm_control_svr/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ description = "A secure RPC server for retrieving and managing system informatio
44
license = "MIT"
55
repository = "https://github.com/davehorner/jetkvm_control"
66
homepage = "https://github.com/davehorner/jetkvm_control"
7-
version = "0.1.3"
87
edition = "2021"
98
authors = ["David Horner"]
109

jetkvm_control_svr/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
This contains the JetKVM Control Secure RPC server, designed to handle requests for system information on a host machine. The server is built using Rust and provides TLS encryption and HMAC authentication to ensure secure and authenticated communication.
44

5+
JetKVM excels at remote mouse and keyboard control, while the jetkvm_control_svr complements this by enabling secure queries for the active process or window on the host machine. This added monitoring capability ensures that your scripts can verify the system’s current state before executing control actions, improving reliability and confidence in automation.
6+
57
## Features
68

79
- **Cross-Platform Compatibility**: Provides access to information about the active process and window on Windows and macOS.

jetkvm_control_svr/jetkvm_control_svr.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,15 @@ struct RpcResponse {
7171
data: serde_json::Value,
7272
}
7373

74-
// Setup the installation of a default cryptographic provider
74+
static CRYPTO_PROVIDER_LOCK: std::sync::OnceLock<()> = std::sync::OnceLock::new();
75+
7576
fn setup_crypto_provider() {
76-
rustls::crypto::aws_lc_rs::default_provider()
77-
.install_default()
78-
.ok();
77+
CRYPTO_PROVIDER_LOCK.get_or_init(|| {
78+
rustls::crypto::ring::default_provider()
79+
.install_default()
80+
.unwrap()
81+
});
82+
// CRYPTO_PROVIDER_LOCK.get_or_init(|| rustls::crypto::aws_lc_rs::default_provider().install_default().ok());
7983
}
8084

8185
#[tokio::main]

lua-examples/windows-alt-tab.lua

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
-- This script simulates an Alt+Tab sequence on Windows.
2+
-- Step 1: Hold down the Alt key.
3+
-- - The combo uses a modifier value of 0x04 (representing Alt) and sends key 0xE2 (the Alt key code).
4+
-- - Both hold_modifiers and hold_keys are enabled so that the Alt key remains pressed.
5+
-- - After sending, it waits 100ms before processing the next combo.
6+
--
7+
-- Step 2: Press the Tab key while keeping Alt held.
8+
-- - The combo still uses the Alt modifier (0x04) and sends key 0x2B (Tab).
9+
-- - The key is held for 50ms and then the script waits 3000ms.
10+
--
11+
-- Step 3: Simulate repeated Tab presses while Alt remains held.
12+
-- - A sequence of combos sends Tab (0x2B) with a hold period (e.g., 200ms) and then sends an empty key press with a wait period (e.g., 2000ms).
13+
--
14+
-- Step 4: Press Esc to cancel the switcher and then release Alt.
15+
-- - First, a combo sends Esc (0x29) with an instant_release flag.
16+
-- - Finally, a combo with modifier 0x04 sends Alt (0xE2) with instant_release to clear the Alt hold.
17+
--
18+
-- The expectation is that during the entire sequence, Alt (0xE2) remains held until we explicitly release it.
19+
-- The log should show active_modifiers = 0x04 while Alt is held and active_keys containing {226}.
20+
-- Finally, after the instant releases, both active_modifiers and active_keys should be cleared.
21+
22+
23+
-- send_key_combinations parameters documentation:
24+
-- ----------------------------------------------
25+
-- send_key_combinations accepts a table (list) of key combo elements.
26+
-- Each element in the table represents a keystroke (or a set of keystrokes) and includes parameters
27+
-- that define how that keystroke is simulated. Here's a breakdown of each parameter:
28+
--
29+
--
30+
-- modifier:
31+
-- - A bitmask representing modifier keys (e.g., 0x04 for Alt).
32+
-- - This value is combined with the keys provided to simulate simultaneous key presses.
33+
--
34+
-- keys:
35+
-- - A table of keycodes (in hexadecimal) to be sent.
36+
-- - Example: { 0xE2 } for the Alt key or { 0x2B } for the Tab key.
37+
-- - If omitted (nil), it can default to an empty table, which is useful for pure waits.
38+
--
39+
-- hold_keys:
40+
-- - A boolean flag.
41+
-- - When true, the keys in this combo are added to the active_keys state and remain pressed until explicitly released.
42+
-- - This is used when you want to keep a key (like Alt) pressed across multiple combos.
43+
--
44+
-- hold_modifiers:
45+
-- - A boolean flag.
46+
-- - When true, the modifier bits (e.g., 0x04 for Alt) are added to the active_modifiers state and are not cleared automatically.
47+
-- - This ensures that even if subsequent combos release keys, the modifier (Alt) stays active.
48+
--
49+
-- hold:
50+
-- - An optional number (milliseconds) that specifies how long to hold the key press before releasing it.
51+
-- - During this period, the keys (and possibly modifiers) remain active.
52+
--
53+
-- wait:
54+
-- - An optional number (milliseconds) to wait after processing the current combo before proceeding to the next combo.
55+
-- - Useful for adding delays between key sequences (e.g., waiting for the switcher UI to appear).
56+
--
57+
-- instant_release:
58+
-- - A boolean flag.
59+
-- - When true, the specified keys (and possibly modifiers) are immediately released after being processed.
60+
-- - The release logic can clear the keys/modifiers unless they are being held by hold_keys/hold_modifiers.
61+
--
62+
-- clear_keys:
63+
-- - An optional boolean flag.
64+
-- - When true, it forces an immediate reset by clearing all active keys and modifiers.
65+
--
66+
--
67+
--
68+
--
69+
--
70+
-- How and why the Alt key remains pressed:
71+
-- - In the first combo, both hold_keys and hold_modifiers are set to true.
72+
-- - The Alt key is sent using its keycode (0xE2) along with the Alt modifier (0x04).
73+
-- - hold_keys ensures that the keycode 0xE2 is retained in the active_keys set.
74+
-- - hold_modifiers ensures that the modifier 0x04 is retained in the active_modifiers set.
75+
-- - This persistent state keeps Alt pressed across subsequent combos until a later combo explicitly releases it.
76+
77+
78+
79+
-- Example script simulating Alt+Tab with detailed timing and release:
80+
send_key_combinations({
81+
-- Step 1: Hold Alt (0xE2)
82+
-- - Send Alt key with modifier 0x04.
83+
-- - Both hold_modifiers and hold_keys are enabled so that Alt remains pressed.
84+
{ modifier = 0x04, keys = { 0xE2 }, wait = 100, hold_modifiers = true, hold_keys = true },
85+
86+
-- Step 2: Press Tab (0x2B) while keeping Alt held.
87+
-- - Sends Tab while Alt remains active.
88+
-- - The Tab key is held for 50ms and then released, leaving Alt still pressed.
89+
{ modifier = 0x04, keys = { 0x2B }, hold = 50, wait = 3000, hold_modifiers = true },
90+
91+
-- Step 3: Additional Tab presses to simulate switching windows.
92+
{ modifier = 0x04, keys = { 0x2B }, hold = 200 },
93+
{ modifier = 0x04, wait = 2000 }, -- pure wait
94+
{ modifier = 0x04, keys = { 0x2B }, hold = 200 },
95+
{ modifier = 0x04, wait = 2000 },
96+
97+
-- Step 4: Press Esc (0x29) to cancel the switcher.
98+
{ modifier = 0x00, keys = { 0x29 }, instant_release = true },
99+
100+
-- Step 5: Release Alt.
101+
-- - Sends the Alt key with modifier 0x04 and instant_release to clear the held Alt state.
102+
{ modifier = 0x04, keys = { 0xE2 }, instant_release = true },
103+
})
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
--[[
2+
This Lua script demonstrates how to connect to the JetKVM control server,
3+
authenticate, fetch details of the active process on the remote system,
4+
and then conditionally execute commands based on the active process.
5+
6+
Breakdown of the script:
7+
8+
1. Create a JetKVM control server client:
9+
- The JetKvmControlSvrClient() constructor initializes a new client instance.
10+
11+
2. Connect to the server:
12+
- svr:connect(host, port, username, certificate_path) attempts to connect to the server.
13+
- It returns a boolean 'success' flag and a 'message' with additional details or error information.
14+
15+
3. Check connection/authentication:
16+
- If the connection fails (success is false), the script prints an error message and terminates.
17+
18+
4. Fetch active process details:
19+
- svr:send_command("active_process") sends a command to retrieve details about the active process on the remote system.
20+
- The result should include an 'executable_name' field if successful.
21+
22+
5. Validate the result:
23+
- If the response is missing or the active process details cannot be determined,
24+
the script prints an error message and exits.
25+
26+
6. Process the active process details:
27+
- The script prints the name of the active process.
28+
- If the active process is 'cmd.exe' (Command Prompt), it sends a text command "echo hello world"
29+
followed by a simulated Return key press to execute the command.
30+
- Otherwise, it prints a message indicating that cmd.exe is not the active process.
31+
32+
Important functions used:
33+
- send_text(text): Sends text input as simulated keystrokes to the remote system.
34+
- send_return(): Simulates pressing the Return (Enter) key to execute a command.
35+
--]]
36+
37+
-- Create the JetKVM control server client
38+
local svr = JetKvmControlSvrClient()
39+
40+
-- Attempt to connect to the server using the provided host, port, username, and certificate path.
41+
-- 'localhost' and '8080' specify the server address, 'dave' is the username, and CERT_PATH is the path to the certificate.
42+
local success, message = svr:connect("localhost", "8080", "dave", CERT_PATH)
43+
print("Connect result:", success, "Message:", message)
44+
45+
-- Check if authentication failed. If not successful, print the error message and exit the script.
46+
if not success then
47+
print("Failed to authenticate:", message)
48+
return
49+
end
50+
51+
-- Fetch the active process details from the remote system.
52+
-- This command should return a table containing details about the active process,
53+
-- including at least an 'executable_name' field.
54+
local result = svr:send_command("active_process")
55+
56+
-- Ensure the response is valid. If the result is nil or does not include an executable name,
57+
-- print an error message and exit.
58+
if not result or not result.executable_name then
59+
print("Failed to retrieve active process details.")
60+
return
61+
end
62+
63+
-- Print the active process's executable name.
64+
print("Executable Name:", result.executable_name)
65+
66+
-- Check if the active process is the Command Prompt (cmd.exe).
67+
-- Convert the executable name to lower case to ensure case-insensitive comparison.
68+
if result.executable_name:lower() == "cmd.exe" then
69+
print("Command Prompt (cmd.exe) is the active process!")
70+
-- If cmd.exe is active, send a text command "echo hello world" to the command prompt.
71+
send_text("echo hello world")
72+
-- Simulate a Return key press to execute the command.
73+
send_return()
74+
else
75+
-- If the active process is not cmd.exe, inform the user by printing the current active process.
76+
print("Command Prompt (cmd.exe) is NOT the active process. Current:", result.executable_name)
77+
end

0 commit comments

Comments
 (0)