Merge remote-tracking branch 'upstream/master' into forapi
# Conflicts: # .github/workflows/build.yaml # .github/workflows/ghcr.yml # README-DE.md # README-NL.md # README-TW.md # README.md # libs/hbb_common # src/main.rs # src/rendezvous_server.rs
This commit is contained in:
commit
916999abd3
24 files changed
+3326
-450
No files matched your search
@@ -1,11 +0,0 @@
|
|||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "gitsubmodule"
|
|
||||||
directory: "/"
|
|
||||||
target-branch: "master"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
commit-message:
|
|
||||||
prefix: "Git submodule"
|
|
||||||
labels:
|
|
||||||
- "dependencies"
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
name: test
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "master" ]
|
|
||||||
paths-ignore:
|
|
||||||
- '**/README.md'
|
|
||||||
pull_request:
|
|
||||||
branches: [ "master" ]
|
|
||||||
paths-ignore:
|
|
||||||
- '**/README.md'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
|
||||||
override: true
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
|
||||||
override: true
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: --all
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
|
||||||
override: true
|
|
||||||
components: rustfmt
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: fmt
|
|
||||||
args: --all -- --check
|
|
||||||
|
|
||||||
clippy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
|
||||||
override: true
|
|
||||||
components: clippy
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: clippy
|
|
||||||
args: --all -- -D warnings
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Repository Instructions
|
||||||
|
|
||||||
|
## Rust Rules
|
||||||
|
|
||||||
|
- In Rust code, do not introduce `unwrap()` or `expect()`.
|
||||||
|
- Allowed exceptions:
|
||||||
|
- Tests may use `unwrap()` or `expect()` when it keeps the test focused and readable.
|
||||||
|
- Lock acquisition may use `unwrap()` only when the locking API makes that the practical option and the failure mode is poison handling rather than normal control flow.
|
||||||
|
- Outside those exceptions, propagate errors, handle them explicitly, or use safer fallbacks instead of `unwrap()` and `expect()`.
|
||||||
|
|
||||||
|
## Editing Hygiene
|
||||||
|
|
||||||
|
- Do not introduce formatting-only changes.
|
||||||
|
- Do not run repository-wide formatters or reflow unrelated code unless the
|
||||||
|
user explicitly asks for formatting.
|
||||||
|
- Keep diffs limited to semantic changes required for the task.
|
||||||
|
|
||||||
Generated
+2330
-265
File diff suppressed because it is too large.
Load diff
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "hbbs"
|
name = "hbbs"
|
||||||
version = "1.1.14"
|
version = "1.1.17"
|
||||||
authors = ["rustdesk <info@rustdesk.com>"]
|
authors = ["rustdesk <info@rustdesk.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
@@ -51,6 +51,7 @@ ipnetwork = "0.20"
|
|||||||
local-ip-address = "0.5.1"
|
local-ip-address = "0.5.1"
|
||||||
dns-lookup = "1.0.8"
|
dns-lookup = "1.0.8"
|
||||||
ping = "0.4.0"
|
ping = "0.4.0"
|
||||||
|
flate2 = "1.0"
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
|
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
|
||||||
# https://github.com/rustdesk/rustdesk-server-pro/issues/189, using native-tls for better tls support
|
# https://github.com/rustdesk/rustdesk-server-pro/issues/189, using native-tls for better tls support
|
||||||
|
|||||||
Binary file not shown.
Vendored
+62
@@ -0,0 +1,62 @@
|
|||||||
|
#
|
||||||
|
# Juli Augustus 2025 was building the downloadable Debian packages done
|
||||||
|
# by github.com CI, meaning you being depended on github.com
|
||||||
|
# for getting a .deb of the libre source code (four GNU freedoms) you have.
|
||||||
|
#
|
||||||
|
# And in those days there was no official Debian package.
|
||||||
|
#
|
||||||
|
# What follows are instructions that make it possible to do
|
||||||
|
# a succesfull
|
||||||
|
#
|
||||||
|
# debuild -uc -us
|
||||||
|
#
|
||||||
|
# in the directory target/rustdesk-server
|
||||||
|
#
|
||||||
|
# The instructions are written in bash syntax,
|
||||||
|
#
|
||||||
|
# bash debian/README.source
|
||||||
|
#
|
||||||
|
# should help you immens
|
||||||
|
#
|
||||||
|
|
||||||
|
# Make sure the submodules are present
|
||||||
|
git submodule update --init --recursive
|
||||||
|
|
||||||
|
rm -rf target/rustdesk-server # avoid clutter from previous iteration
|
||||||
|
mkdir -p target/rustdesk-server # FYI: target/ is ignored by git
|
||||||
|
|
||||||
|
tar cf - \
|
||||||
|
--exclude .git \
|
||||||
|
Cargo.toml Cargo.lock \
|
||||||
|
LICENSE README.md \
|
||||||
|
db_v2.sqlite3 \
|
||||||
|
build.rs \
|
||||||
|
debian \
|
||||||
|
libs rcd src \
|
||||||
|
systemd ui \
|
||||||
|
| ( cd target/rustdesk-server && tar x )
|
||||||
|
|
||||||
|
mv target/rustdesk-server/debian/Makefile target/rustdesk-server/
|
||||||
|
|
||||||
|
tar cjf target/rustdesk-server-orig.tar.xz \
|
||||||
|
--strip-components=1 \
|
||||||
|
target/rustdesk-server
|
||||||
|
|
||||||
|
# an .orig.tar.xz tarball exists,
|
||||||
|
# work on the debian directory
|
||||||
|
|
||||||
|
eval $( dpkg-architecture )
|
||||||
|
sed -e "s/{{ ARCH }}/${DEB_TARGET_ARCH}/" \
|
||||||
|
debian/control.tpl > target/rustdesk-server/debian/control
|
||||||
|
|
||||||
|
|
||||||
|
cd target/rustdesk-server
|
||||||
|
dpkg-checkbuilddeps || echo sudo apt install dpkg-dev
|
||||||
|
debuild -uc -us
|
||||||
|
|
||||||
|
#
|
||||||
|
# For what it is worth:
|
||||||
|
# Early September 2025 there were
|
||||||
|
# several WARNINGS and ERRORS from Lintian
|
||||||
|
#
|
||||||
|
# l l
|
||||||
Vendored
+44
@@ -1,36 +1,80 @@
|
|||||||
|
rustdesk-server (1.1.17) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* Fix more UDP reflection/amplification
|
||||||
|
|
||||||
|
rustdesk-server (1.1.16) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* Fix i32 overflow: peers offline 24.9–49.7 days wrongly reported online #676
|
||||||
|
* Fix critical issue in mio via minor version upgrade from 0.8.5 to 0.8.11 in ui
|
||||||
|
* Fix unauthenticated UDP punch-hole request enables reflection/amplification abuse
|
||||||
|
|
||||||
|
rustdesk-server (1.1.15) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* Fix: 127.0.0.1 is not loopback (#515)
|
||||||
|
* Higher default bandwidth
|
||||||
|
* Define the HOME env to allow running rootless (#612)
|
||||||
|
* Add option to log (failed) authentication attempts to enable the usage of tools like fail2ban and crowdsec (#435)
|
||||||
|
* Connection log query
|
||||||
|
|
||||||
|
-- rustdesk <info@rustdesk.com> Sat, 10 Jan 2026 16:04:19 +0800
|
||||||
|
|
||||||
rustdesk-server (1.1.14) UNRELEASED; urgency=medium
|
rustdesk-server (1.1.14) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* Fix windows crash
|
* Fix windows crash
|
||||||
|
|
||||||
|
-- rustdesk <info@rustdesk.com> Sat, 25 Jan 2025 20:47:19 +0800
|
||||||
|
|
||||||
rustdesk-server (1.1.13) UNRELEASED; urgency=medium
|
rustdesk-server (1.1.13) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* Version check and refactor hbb_common to share with rustdesk client
|
* Version check and refactor hbb_common to share with rustdesk client
|
||||||
|
|
||||||
|
-- rustdesk <info@rustdesk.com> Tue, 21 Jan 2025 01:33:42 +0800
|
||||||
|
|
||||||
rustdesk-server (1.1.12) UNRELEASED; urgency=medium
|
rustdesk-server (1.1.12) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* WS real ip
|
* WS real ip
|
||||||
* Bump s6-overlay to v3.2.0.0 and fix env warnings
|
* Bump s6-overlay to v3.2.0.0 and fix env warnings
|
||||||
|
|
||||||
|
-- rustdesk <info@rustdesk.com> Mon, 7 Oct 2024 16:21:36 +0800
|
||||||
|
|
||||||
rustdesk-server (1.1.11-1) UNRELEASED; urgency=medium
|
rustdesk-server (1.1.11-1) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* set reuse port to make restart friendly
|
* set reuse port to make restart friendly
|
||||||
* revert hbbr `-k` to not ruin back-compatibility
|
* revert hbbr `-k` to not ruin back-compatibility
|
||||||
|
|
||||||
|
-- rustdesk <info@rustdesk.com> Fri, 24 May 2024 18:37:11 +0800
|
||||||
|
|
||||||
rustdesk-server (1.1.11) UNRELEASED; urgency=medium
|
rustdesk-server (1.1.11) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* change -k to default '-', so you need not to set -k any more
|
* change -k to default '-', so you need not to set -k any more
|
||||||
|
|
||||||
|
-- rustdesk <info@rustdesk.com> Fri, 24 May 2024 18:09:12 +0800
|
||||||
|
|
||||||
rustdesk-server (1.1.10-3) UNRELEASED; urgency=medium
|
rustdesk-server (1.1.10-3) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* fix on -2
|
* fix on -2
|
||||||
|
|
||||||
|
-- rustdesk <info@rustdesk.com> Wed, 31 Jan 2024 11:30:42 +0800
|
||||||
|
|
||||||
rustdesk-server (1.1.10-2) UNRELEASED; urgency=medium
|
rustdesk-server (1.1.10-2) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* fix hangup signal exit when run with nohup
|
* fix hangup signal exit when run with nohup
|
||||||
* some minors
|
* some minors
|
||||||
|
|
||||||
|
-- rustdesk <info@rustdesk.com> Tue, 30 Jan 2024 19:23:36 +0800
|
||||||
|
|
||||||
rustdesk-server (1.1.9) UNRELEASED; urgency=medium
|
rustdesk-server (1.1.9) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* remove unsafe
|
* remove unsafe
|
||||||
|
|
||||||
|
-- rustdesk <info@rustdesk.com> Tue, 5 Dec 2023 17:22:40 +0800
|
||||||
|
|
||||||
rustdesk-server (1.1.8) UNRELEASED; urgency=medium
|
rustdesk-server (1.1.8) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* fix test_hbbs and mask in lan
|
* fix test_hbbs and mask in lan
|
||||||
|
|
||||||
|
-- rustdesk <info@rustdesk.com> Thu, 6 Jul 2023 00:50:11 +0800
|
||||||
|
|
||||||
rustdesk-server (1.1.7) UNRELEASED; urgency=medium
|
rustdesk-server (1.1.7) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* ipv6 support
|
* ipv6 support
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ FROM scratch
|
|||||||
COPY hbbs /usr/bin/hbbs
|
COPY hbbs /usr/bin/hbbs
|
||||||
COPY hbbr /usr/bin/hbbr
|
COPY hbbr /usr/bin/hbbr
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
|
ENV HOME=/root
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
# Configuration & Environment Variables
|
||||||
|
|
||||||
|
This document is the single reference for every option that the open‑source
|
||||||
|
RustDesk server binaries (`hbbs`, `hbbr`) understand: command‑line flags,
|
||||||
|
environment variables, and configuration files.
|
||||||
|
|
||||||
|
> **TL;DR** — For most people the command‑line flags shown by `hbbs --help` /
|
||||||
|
> `hbbr --help` are all you need. Environment variables are an alternative way to
|
||||||
|
> set the same options, plus a handful of extra tuning knobs that have no flag.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How configuration is loaded
|
||||||
|
|
||||||
|
Both servers read their configuration from the following sources. For
|
||||||
|
**`hbbs`** the order of precedence, from highest to lowest, is:
|
||||||
|
|
||||||
|
1. **Command‑line flag** (e.g. `-p 21116`, `-k mykey`)
|
||||||
|
2. **`--config <file>`** — an INI file passed with `-c`/`--config`
|
||||||
|
3. **`.env`** — an INI file named `.env` in the working directory
|
||||||
|
4. **Inherited process environment** — variables exported before launch
|
||||||
|
|
||||||
|
A value set by a higher source overrides the same value from a lower one. Under
|
||||||
|
the hood every source is turned into a process environment variable, and the
|
||||||
|
code then reads that variable — so "flag", "config file" and "env var" are just
|
||||||
|
three ways to set the same thing.
|
||||||
|
|
||||||
|
For **`hbbr`** the precedence is: **flag** (`-b`, `-p`, `-k`) → **`.env`** →
|
||||||
|
**inherited environment**.
|
||||||
|
|
||||||
|
`RUST_LOG` is an exception to these rules. Both binaries initialize logging
|
||||||
|
before loading `.env` (or `hbbs`'s `--config` file), so `RUST_LOG` must be set
|
||||||
|
in the inherited process environment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `hbbs` — ID / rendezvous server
|
||||||
|
|
||||||
|
| Variable | CLI flag | Default | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `KEY` | `-k`, `--key` | `-` | Public key clients must use, a base64 secret key, or `-` / `_` to load or generate a key pair (`id_ed25519`, `id_ed25519.pub`). `-` and `_` have the same behavior, so explicitly passing `-k _` to `hbbs` is unnecessary. An explicitly empty value disables key validation; see [Keys](#keys-and-encryption). |
|
||||||
|
| `BIND` | `-b`, `--bind` | all interfaces | **Available since 1.1.17.** Local IPv4 or IPv6 address on which all `hbbs` TCP, UDP, and WebSocket listeners bind. This does not change the addresses advertised to clients. Supported by `--config`, `.env`, and the inherited environment. |
|
||||||
|
| `PORT` | `-p`, `--port` | `21116` | Main TCP/UDP listening port. `hbbs` also binds `PORT-1` (NAT type test) and `PORT+2` (WebSocket). |
|
||||||
|
| `RELAY-SERVERS` | `-r`, `--relay-servers` | *(empty)* | Optional relay server override handed to clients, as comma-separated `host` or `host:port` values. Leave empty when `hbbr` uses the same address as `hbbs` and the standard port `21117`; clients derive it automatically. Set this only when the relay uses a different IP/hostname or a non-standard port. |
|
||||||
|
| `RMEM` | `-M`, `--rmem` | `0` (system default) | UDP receive‑buffer size in bytes. Raise the OS limit first: `sudo sysctl -w net.core.rmem_max=52428800`. |
|
||||||
|
| *(config file)* | `-c`, `--config` | *(none)* | Path to an extra INI config file (see precedence above). |
|
||||||
|
| `TEST_HBBS` 🅴 | *(none)* | *(auto)* | UDP self‑test target checked at start‑up. Set to `no` to skip the check (useful behind some NATs/proxies), or to an explicit `host:port`. |
|
||||||
|
| `ALWAYS_USE_RELAY` 🅴 | *(none)* | `N` | `Y` forces every session through a relay (disables direct/hole‑punched connections). At runtime, send `always-use-relay Y` or `always-use-relay N` to the `hbbs` [loopback console](#runtime-console). |
|
||||||
|
| `DB_URL` 🅴 | *(none)* | `./db_v2.sqlite3` | Path/URL of the SQLite database file. See [Database](#database). |
|
||||||
|
| `MAX_DATABASE_CONNECTIONS` 🅴 | *(none)* | `1` | Size of the SQLite connection pool. |
|
||||||
|
|
||||||
|
🅴 = set through the inherited process environment.
|
||||||
|
|
||||||
|
> `PORT_FOR_API` / `KEY_FOR_API` are only used by RustDesk Server **Pro** and its
|
||||||
|
> API; they have no effect in the open‑source server.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `hbbr` — relay server
|
||||||
|
|
||||||
|
| Variable | CLI flag | Default | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `KEY` | `-k`, `--key` | *(empty)* | The empty default intentionally disables relay key validation, avoiding key-pair setup and mismatch failures. To enable relay key validation, use the same non-empty key as `hbbs`; `-` / `_` have the same behavior and load or generate a key pair. An empty key allows clients without a matching key to use the relay, so choose this tradeoff deliberately on an exposed server. |
|
||||||
|
| `BIND` | `-b`, `--bind` | all interfaces | **Available since 1.1.17.** Local IPv4 or IPv6 address on which the relay TCP and WebSocket listeners bind. Supported by `.env` and the inherited environment; `hbbr` does not support `--config`. |
|
||||||
|
| `PORT` | `-p`, `--port` | `21117` | Relay listening port. `hbbr` also binds `PORT+2` for WebSocket relay. **Note:** when set via the `PORT` env var (not `-p`), `hbbr` listens on `PORT + 1`, so a shared `PORT=21116` makes `hbbs`=21116 and `hbbr`=21117. |
|
||||||
|
|
||||||
|
### Relay bandwidth / QoS
|
||||||
|
|
||||||
|
These have no CLI flag and can also be changed through the `hbbr`
|
||||||
|
[loopback console](#runtime-console) (`tb`, `sb`, `ls`, `dt`, `t`, …; send `h`
|
||||||
|
for help).
|
||||||
|
|
||||||
|
| Variable | Default | Unit | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `SINGLE_BANDWIDTH` | `128` | Mb/s | Normal maximum bandwidth for each relay connection. |
|
||||||
|
| `TOTAL_BANDWIDTH` | `1024` | Mb/s | Aggregate bandwidth cap shared by all relay connections. |
|
||||||
|
| `LIMIT_SPEED` | `32` | Mb/s | Per-connection cap applied after a connection is downgraded, and to IPs in `blacklist.txt`. |
|
||||||
|
| `DOWNGRADE_THRESHOLD` | `0.66` | ratio (0–1) | Fraction of `SINGLE_BANDWIDTH` that a connection's lifetime-average throughput must exceed to trigger downgrade. |
|
||||||
|
| `DOWNGRADE_START_CHECK` | `1800` | seconds | Delay before a connection becomes eligible for the lifetime-average downgrade check. |
|
||||||
|
|
||||||
|
Downgrade is decided independently for each connection; it does **not** check
|
||||||
|
aggregate relay congestion. After `DOWNGRADE_START_CHECK`, a connection is
|
||||||
|
capped to `LIMIT_SPEED` once its average throughput since it started exceeds
|
||||||
|
`SINGLE_BANDWIDTH * DOWNGRADE_THRESHOLD`. A lone transfer can therefore be
|
||||||
|
downgraded even when the relay is otherwise idle. `TOTAL_BANDWIDTH` is a
|
||||||
|
separate aggregate cap.
|
||||||
|
|
||||||
|
These may also be placed in `.env` using the uppercase spellings shown above
|
||||||
|
(e.g. `SINGLE_BANDWIDTH=256`).
|
||||||
|
|
||||||
|
### Blocklists / blacklists (files, not env vars)
|
||||||
|
|
||||||
|
`hbbr` reads two optional files from its working directory at start‑up:
|
||||||
|
|
||||||
|
* **`blacklist.txt`** — IPs that are **bandwidth‑limited** (one IP per line;
|
||||||
|
anything after the first space on a line is ignored).
|
||||||
|
* **`blocklist.txt`** — IPs that are **refused** outright.
|
||||||
|
|
||||||
|
Both can also be edited live through the `hbbr` loopback console (`ba`/`br`,
|
||||||
|
`Ba`/`Br`).
|
||||||
|
|
||||||
|
### Runtime console
|
||||||
|
|
||||||
|
The runtime consoles are TCP command transports built into the services; they
|
||||||
|
are not `rustdesk-utils` commands or interactive standard-input consoles. A
|
||||||
|
connection from a loopback address is treated as a single console command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# hbbs: toggle forced relay on PORT-1 (21115 by default)
|
||||||
|
printf 'always-use-relay Y' | nc 127.0.0.1 21115
|
||||||
|
|
||||||
|
# hbbr: list commands on its relay PORT (21117 by default)
|
||||||
|
printf 'h' | nc 127.0.0.1 21117
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the corresponding configured ports if you changed `PORT`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Database
|
||||||
|
|
||||||
|
At runtime the database location comes from **`DB_URL`** (default
|
||||||
|
`./db_v2.sqlite3`). If unset, `hbbs` creates the SQLite file in its working
|
||||||
|
directory.
|
||||||
|
|
||||||
|
> **Do not confuse `DB_URL` with `DATABASE_URL`.** The `DATABASE_URL` entry in
|
||||||
|
> the repository's `.env` is used **only at compile time** by `sqlx` to check SQL
|
||||||
|
> queries; it is **not** read by the running server. Setting `DATABASE_URL` on a
|
||||||
|
> running server has no effect — use `DB_URL`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
Both binaries use `flexi_logger`, which honours the standard **`RUST_LOG`**
|
||||||
|
environment variable (default level `info`). Set it in the process environment
|
||||||
|
before launching the binary. A value in `.env` or `hbbs`'s `--config` file is
|
||||||
|
loaded too late and has no effect on logging.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RUST_LOG=debug hbbs
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Keys and encryption
|
||||||
|
|
||||||
|
The `KEY` / `-k` value can be:
|
||||||
|
|
||||||
|
* a **public key** string — clients must present the matching key;
|
||||||
|
* a **base64‑encoded 64‑byte secret key** — the server derives the public key
|
||||||
|
from it;
|
||||||
|
* **`-` or `_`** — the server loads a key pair from the working directory or
|
||||||
|
generates one on first start, writing `id_ed25519` (private) and
|
||||||
|
`id_ed25519.pub` (public);
|
||||||
|
* **empty** — key validation is disabled. `hbbs` still loads or generates key
|
||||||
|
files for signing but deliberately leaves its active validation key empty;
|
||||||
|
`hbbr` neither loads nor generates a key. Both services then accept clients
|
||||||
|
without validating a key. This is the intentional `hbbr` default; use a
|
||||||
|
non-empty value when relay key validation is required.
|
||||||
|
|
||||||
|
`hbbs` defaults to `-`, so it already loads or generates a key pair without an
|
||||||
|
explicit `-k _`. `hbbr` intentionally defaults to an empty key to avoid
|
||||||
|
key-pair setup and mismatch failures. Leave it empty for the default mode
|
||||||
|
without key validation. To enable relay key validation, give it the same
|
||||||
|
non-empty key as `hbbs`; both services can reuse key material from a shared
|
||||||
|
working directory. The `_` value is not a stricter mode than `-` in the current
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
To supply your own key pair, place `id_ed25519` and `id_ed25519.pub` in the
|
||||||
|
process's **current working directory** before first start. That directory may
|
||||||
|
differ from the directory containing the executable. For the supervisor Docker
|
||||||
|
image, the working directory is `/data`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Docker image variables
|
||||||
|
|
||||||
|
The supervisor image (`rustdesk/rustdesk-server-s6`) starts both binaries with
|
||||||
|
s6 and adds a few convenience variables handled by its service scripts, **not**
|
||||||
|
by `hbbs`/`hbbr` directly:
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `RELAY` | `relay.example.com` | Passed to `hbbs` as `-r $RELAY` (your public address). |
|
||||||
|
| `ENCRYPTED_ONLY` | `0` | `1` adds `-k _` to both servers. This is redundant for `hbbs`, whose default is `-`, and opts `hbbr` into key validation instead of its intentional empty default. |
|
||||||
|
| `KEY_PUB` | *(unset)* | If set, written to `/data/id_ed25519.pub` on first start. |
|
||||||
|
| `KEY_PRIV` | *(unset)* | If set, written to `/data/id_ed25519` on first start. Provide **both** `KEY_PUB` and `KEY_PRIV`, or neither. |
|
||||||
|
|
||||||
|
Any variable from the tables above can also be passed straight through the
|
||||||
|
container's environment (e.g. `-e ALWAYS_USE_RELAY=Y`, `-e RUST_LOG=debug`).
|
||||||
|
|
||||||
|
The classic scratch image (`rustdesk/rustdesk-server`) contains only the
|
||||||
|
binaries and does **not** implement `RELAY`, `ENCRYPTED_ONLY`, `KEY_PUB`, or
|
||||||
|
`KEY_PRIV`; those variables are ignored by that image.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Command line — non-standard ports
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Tell clients where the relay listens because it is not using port 21117.
|
||||||
|
hbbs -p 22116 -r rustdesk.example.com:22117
|
||||||
|
hbbr -p 22117
|
||||||
|
```
|
||||||
|
|
||||||
|
### `.env` file (working directory)
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# Non-standard ports shared by both binaries; hbbr listens on PORT+1.
|
||||||
|
relay-servers=rustdesk.example.com:22117
|
||||||
|
PORT=22116
|
||||||
|
```
|
||||||
|
|
||||||
|
### docker-compose
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
rustdesk-server:
|
||||||
|
image: rustdesk/rustdesk-server-s6:latest
|
||||||
|
environment:
|
||||||
|
- RELAY=rustdesk.example.com:21117
|
||||||
|
- ALWAYS_USE_RELAY=Y
|
||||||
|
- RUST_LOG=info
|
||||||
|
- SINGLE_BANDWIDTH=256
|
||||||
|
ports:
|
||||||
|
- "21115:21115"
|
||||||
|
- "21116:21116"
|
||||||
|
- "21116:21116/udp"
|
||||||
|
- "21117:21117"
|
||||||
|
- "21118:21118"
|
||||||
|
- "21119:21119"
|
||||||
|
volumes: ["./data:/data"]
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
### systemd
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Service]
|
||||||
|
Environment=ALWAYS_USE_RELAY=Y
|
||||||
|
Environment=RUST_LOG=info
|
||||||
|
ExecStart=/usr/bin/hbbs
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Port reference
|
||||||
|
|
||||||
|
| Port | Proto | Server | Purpose |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 21115 | TCP | hbbs | NAT type test (`PORT-1`) |
|
||||||
|
| 21116 | TCP + UDP | hbbs | ID registration / rendezvous / hole punching (`PORT`) |
|
||||||
|
| 21117 | TCP | hbbr | Relay (`hbbr PORT`) |
|
||||||
|
| 21118 | TCP | hbbs | WebSocket rendezvous (`PORT+2`) |
|
||||||
|
| 21119 | TCP | hbbr | WebSocket relay (`hbbr PORT+2`) |
|
||||||
|
|
||||||
|
Ports 21118/21119 are only needed for the web client; you can omit them
|
||||||
|
otherwise.
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: rustdesk-server
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: rustdesk
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: rustdesk
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
# id Server (HBBS)
|
||||||
|
- name: hbbs
|
||||||
|
image: rustdesk/rustdesk-server:latest
|
||||||
|
env:
|
||||||
|
- name: RELAY_HOST
|
||||||
|
value: "rustdesk.yourdomain.tld"
|
||||||
|
command: ["hbbs", "-r", "$(RELAY_HOST):21117", "-k", "_"]
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /root
|
||||||
|
name: rustdesk-data
|
||||||
|
ports:
|
||||||
|
- containerPort: 21115
|
||||||
|
- containerPort: 21116
|
||||||
|
protocol: TCP
|
||||||
|
- containerPort: 21116
|
||||||
|
protocol: UDP
|
||||||
|
- containerPort: 21118
|
||||||
|
|
||||||
|
# Relay Server (HBBR)
|
||||||
|
- name: hbbr
|
||||||
|
image: rustdesk/rustdesk-server:latest
|
||||||
|
command: ["hbbr", "-k", "_"]
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /root
|
||||||
|
name: rustdesk-data
|
||||||
|
ports:
|
||||||
|
- containerPort: 21117
|
||||||
|
- containerPort: 21119
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: rustdesk-data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: rustdesk-pvc
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: rustdesk-service
|
||||||
|
spec:
|
||||||
|
type: LoadBalancer # Or NodePort
|
||||||
|
selector:
|
||||||
|
app: rustdesk
|
||||||
|
ports:
|
||||||
|
- name: hbbs-tcp
|
||||||
|
port: 21115
|
||||||
|
targetPort: 21115
|
||||||
|
protocol: TCP
|
||||||
|
- name: hbbs-udp
|
||||||
|
port: 21116
|
||||||
|
targetPort: 21116
|
||||||
|
protocol: UDP
|
||||||
|
- name: hbbs-tcp-main
|
||||||
|
port: 21116
|
||||||
|
targetPort: 21116
|
||||||
|
protocol: TCP
|
||||||
|
- name: hbbr-tcp
|
||||||
|
port: 21117
|
||||||
|
targetPort: 21117
|
||||||
|
protocol: TCP
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: rustdesk-pvc
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce # RWM would be available only with pro version
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
+1
-1
Submodule libs/hbb_common updated: d6b14975ff...69cea8dafe.
+189
-5
@@ -7,10 +7,68 @@ use sodiumoxide::crypto::sign;
|
|||||||
use std::{
|
use std::{
|
||||||
io::prelude::*,
|
io::prelude::*,
|
||||||
io::Read,
|
io::Read,
|
||||||
net::SocketAddr,
|
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||||
time::{Instant, SystemTime},
|
time::{Instant, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn parse_bind_address(value: &str) -> Result<Option<IpAddr>> {
|
||||||
|
let value = value.trim();
|
||||||
|
if value.is_empty() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
value
|
||||||
|
.parse()
|
||||||
|
.with_context(|| format!("Invalid bind address: {value}"))
|
||||||
|
.map(Some)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn listen_tcp(
|
||||||
|
bind_addr: Option<IpAddr>,
|
||||||
|
port: u16,
|
||||||
|
) -> ResultType<hbb_common::tokio::net::TcpListener> {
|
||||||
|
if let Some(bind_addr) = bind_addr {
|
||||||
|
hbb_common::tcp::new_listener(SocketAddr::new(bind_addr, port), true).await
|
||||||
|
} else {
|
||||||
|
hbb_common::tcp::listen_any(port).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn console_addr(bind_addr: Option<IpAddr>, port: u16) -> Option<SocketAddr> {
|
||||||
|
let bind_addr = bind_addr?;
|
||||||
|
if bind_addr.is_unspecified() || bind_addr == IpAddr::V4(Ipv4Addr::LOCALHOST) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The runtime console (check_cmd) is reached via 127.0.0.1, so when the bind
|
||||||
|
// address does not already accept connections to 127.0.0.1 (it is neither the
|
||||||
|
// any-address nor 127.0.0.1 itself), the console gets a dedicated listener
|
||||||
|
// there; it is never bound to the external bind address.
|
||||||
|
pub async fn listen_console(
|
||||||
|
bind_addr: Option<IpAddr>,
|
||||||
|
port: u16,
|
||||||
|
) -> ResultType<Option<hbb_common::tokio::net::TcpListener>> {
|
||||||
|
match console_addr(bind_addr, port) {
|
||||||
|
Some(addr) => {
|
||||||
|
let listener = hbb_common::tcp::new_listener(addr, true).await?;
|
||||||
|
log::info!("Listening on tcp {} for the console", addr);
|
||||||
|
Ok(Some(listener))
|
||||||
|
}
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn accept_or_pending(
|
||||||
|
listener: Option<&hbb_common::tokio::net::TcpListener>,
|
||||||
|
) -> std::io::Result<(hbb_common::tokio::net::TcpStream, SocketAddr)> {
|
||||||
|
match listener {
|
||||||
|
Some(listener) => listener.accept().await,
|
||||||
|
None => std::future::pending().await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn get_expired_time() -> Instant {
|
pub(crate) fn get_expired_time() -> Instant {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
@@ -52,6 +110,12 @@ fn arg_name(name: &str) -> String {
|
|||||||
name.to_uppercase().replace('_', "-")
|
name.to_uppercase().replace('_', "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[inline]
|
||||||
|
pub fn set_arg(name: &str, value: &str) {
|
||||||
|
std::env::set_var(arg_name(name), value);
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn init_args(args: &str, name: &str, about: &str) {
|
pub fn init_args(args: &str, name: &str, about: &str) {
|
||||||
let matches = App::new(name)
|
let matches = App::new(name)
|
||||||
@@ -64,7 +128,7 @@ pub fn init_args(args: &str, name: &str, about: &str) {
|
|||||||
if let Some(section) = v.section(None::<String>) {
|
if let Some(section) = v.section(None::<String>) {
|
||||||
section
|
section
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|(k, v)| std::env::set_var(arg_name(k), v));
|
.for_each(|(k, v)| set_arg(k, v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(config) = matches.value_of("config") {
|
if let Some(config) = matches.value_of("config") {
|
||||||
@@ -72,17 +136,42 @@ pub fn init_args(args: &str, name: &str, about: &str) {
|
|||||||
if let Some(section) = v.section(None::<String>) {
|
if let Some(section) = v.section(None::<String>) {
|
||||||
section
|
section
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|(k, v)| std::env::set_var(arg_name(k), v));
|
.for_each(|(k, v)| set_arg(k, v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (k, v) in matches.args {
|
for (k, v) in matches.args {
|
||||||
if let Some(v) = v.vals.first() {
|
if let Some(v) = v.vals.first() {
|
||||||
std::env::set_var(arg_name(k), v.to_string_lossy().to_string());
|
set_arg(k, &v.to_string_lossy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_arg_opt(name: &str) -> Option<String> {
|
||||||
|
let dashed = arg_name(name);
|
||||||
|
let underscored = dashed.replace('-', "_");
|
||||||
|
let lower_dashed = dashed.to_lowercase();
|
||||||
|
let lower_underscored = underscored.to_lowercase();
|
||||||
|
for alias in [&dashed, &underscored, &lower_dashed, &lower_underscored] {
|
||||||
|
if let Ok(value) = std::env::var(alias) {
|
||||||
|
return Some(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut aliases = std::env::vars_os()
|
||||||
|
.filter_map(|(key, value)| {
|
||||||
|
let key = key.into_string().ok()?;
|
||||||
|
if arg_name(&key) == dashed {
|
||||||
|
Some((key, value.into_string().ok()?))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
aliases.sort_by(|a, b| a.0.cmp(&b.0));
|
||||||
|
aliases.into_iter().next().map(|(_, value)| value)
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_arg(name: &str) -> String {
|
pub fn get_arg(name: &str) -> String {
|
||||||
@@ -92,7 +181,7 @@ pub fn get_arg(name: &str) -> String {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_arg_or(name: &str, default: String) -> String {
|
pub fn get_arg_or(name: &str, default: String) -> String {
|
||||||
std::env::var(arg_name(name)).unwrap_or(default)
|
get_arg_opt(name).unwrap_or(default)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -216,3 +305,98 @@ async fn check_software_update_() -> hbb_common::ResultType<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn argument_names_ignore_case_and_separator() {
|
||||||
|
let aliases = [
|
||||||
|
"RUSTDESK-CONFIG-ALIAS-TEST",
|
||||||
|
"RUSTDESK_CONFIG_ALIAS_TEST",
|
||||||
|
"rustdesk-config-alias-test",
|
||||||
|
"rustdesk_config_alias_test",
|
||||||
|
"RustDesk_Config-Alias_Test",
|
||||||
|
];
|
||||||
|
for alias in aliases {
|
||||||
|
std::env::remove_var(alias);
|
||||||
|
}
|
||||||
|
for alias in aliases {
|
||||||
|
std::env::set_var(alias, alias);
|
||||||
|
assert_eq!(get_arg("RUSTDESK_CONFIG_ALIAS_TEST"), alias);
|
||||||
|
std::env::remove_var(alias);
|
||||||
|
}
|
||||||
|
set_arg("rustdesk_config_alias_test", "normalized");
|
||||||
|
assert_eq!(
|
||||||
|
std::env::var("RUSTDESK-CONFIG-ALIAS-TEST").unwrap(),
|
||||||
|
"normalized"
|
||||||
|
);
|
||||||
|
std::env::set_var("RUSTDESK_CONFIG_ALIAS_TEST", "inherited");
|
||||||
|
set_arg("rustdesk-config-alias-test", "higher-priority");
|
||||||
|
assert_eq!(get_arg("rustdesk_config_alias_test"), "higher-priority");
|
||||||
|
std::env::remove_var("RUSTDESK-CONFIG-ALIAS-TEST");
|
||||||
|
std::env::remove_var("RUSTDESK_CONFIG_ALIAS_TEST");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_bind_address() {
|
||||||
|
assert_eq!(parse_bind_address("").unwrap(), None);
|
||||||
|
assert_eq!(
|
||||||
|
parse_bind_address("127.0.0.1").unwrap(),
|
||||||
|
Some(IpAddr::V4(Ipv4Addr::LOCALHOST))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_bind_address("::1").unwrap(),
|
||||||
|
Some(IpAddr::V6(Ipv6Addr::LOCALHOST))
|
||||||
|
);
|
||||||
|
assert!(parse_bind_address("not-an-ip").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hbb_common::tokio::test]
|
||||||
|
async fn tcp_listener_uses_bind_address() {
|
||||||
|
let bind_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
|
||||||
|
let listener = listen_tcp(Some(bind_addr), 0).await.unwrap();
|
||||||
|
assert_eq!(listener.local_addr().unwrap().ip(), bind_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn console_addr_only_when_bind_does_not_cover_ipv4_localhost() {
|
||||||
|
for bind_addr in [
|
||||||
|
None,
|
||||||
|
Some(IpAddr::V4(Ipv4Addr::UNSPECIFIED)),
|
||||||
|
Some(IpAddr::V6(Ipv6Addr::UNSPECIFIED)),
|
||||||
|
Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
|
||||||
|
] {
|
||||||
|
assert_eq!(console_addr(bind_addr, 21117), None);
|
||||||
|
}
|
||||||
|
for bind_addr in [
|
||||||
|
Some(IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1))),
|
||||||
|
Some("2001:db8::1".parse().unwrap()),
|
||||||
|
Some(IpAddr::V6(Ipv6Addr::LOCALHOST)),
|
||||||
|
] {
|
||||||
|
assert_eq!(
|
||||||
|
console_addr(bind_addr, 21117),
|
||||||
|
Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 21117))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hbb_common::tokio::test]
|
||||||
|
async fn console_listener_binds_ipv4_localhost() {
|
||||||
|
let listener = listen_console(Some(IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1))), 0)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
listener.local_addr().unwrap().ip(),
|
||||||
|
IpAddr::V4(Ipv4Addr::LOCALHOST)
|
||||||
|
);
|
||||||
|
assert!(listen_console(None, 0).await.unwrap().is_none());
|
||||||
|
assert!(listen_console(Some(IpAddr::V4(Ipv4Addr::LOCALHOST)), 0)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-2
@@ -51,8 +51,7 @@ impl Database {
|
|||||||
if !std::path::Path::new(url).exists() {
|
if !std::path::Path::new(url).exists() {
|
||||||
std::fs::File::create(url).ok();
|
std::fs::File::create(url).ok();
|
||||||
}
|
}
|
||||||
let n: usize = std::env::var("MAX_DATABASE_CONNECTIONS")
|
let n: usize = crate::common::get_arg_or("MAX_DATABASE_CONNECTIONS", "1".to_owned())
|
||||||
.unwrap_or_else(|_| "1".to_owned())
|
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap_or(1);
|
.unwrap_or(1);
|
||||||
log::debug!("MAX_DATABASE_CONNECTIONS={}", n);
|
log::debug!("MAX_DATABASE_CONNECTIONS={}", n);
|
||||||
|
|||||||
+16
-7
@@ -13,7 +13,8 @@ fn main() -> ResultType<()> {
|
|||||||
.write_mode(WriteMode::Async)
|
.write_mode(WriteMode::Async)
|
||||||
.start()?;
|
.start()?;
|
||||||
let args = format!(
|
let args = format!(
|
||||||
"-p, --port=[NUMBER(default={RELAY_PORT})] 'Sets the listening port'
|
"-b, --bind=[IP] 'Sets the IP address to bind to (default: all interfaces)'
|
||||||
|
-p, --port=[NUMBER(default={RELAY_PORT})] 'Sets the listening port'
|
||||||
-k, --key=[KEY] 'Only allow the client with the same key'
|
-k, --key=[KEY] 'Only allow the client with the same key'
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
@@ -25,21 +26,29 @@ fn main() -> ResultType<()> {
|
|||||||
.get_matches();
|
.get_matches();
|
||||||
if let Ok(v) = ini::Ini::load_from_file(".env") {
|
if let Ok(v) = ini::Ini::load_from_file(".env") {
|
||||||
if let Some(section) = v.section(None::<String>) {
|
if let Some(section) = v.section(None::<String>) {
|
||||||
section.iter().for_each(|(k, v)| std::env::set_var(k, v));
|
section.iter().for_each(|(k, v)| common::set_arg(k, v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut port = RELAY_PORT;
|
let mut port = RELAY_PORT;
|
||||||
if let Ok(v) = std::env::var("PORT") {
|
if let Some(v) = common::get_arg_opt("PORT") {
|
||||||
let v: i32 = v.parse().unwrap_or_default();
|
let v: i32 = v.parse().unwrap_or_default();
|
||||||
if v > 0 {
|
if v > 0 {
|
||||||
port = v + 1;
|
port = v + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
start(
|
let bind = matches
|
||||||
|
.value_of("bind")
|
||||||
|
.map(str::to_owned)
|
||||||
|
.unwrap_or_else(|| common::get_arg("BIND"));
|
||||||
|
let bind_addr = common::parse_bind_address(&bind)?;
|
||||||
|
let key = matches
|
||||||
|
.value_of("key")
|
||||||
|
.map(str::to_owned)
|
||||||
|
.unwrap_or_else(|| common::get_arg("KEY"));
|
||||||
|
start_with_bind(
|
||||||
|
bind_addr,
|
||||||
matches.value_of("port").unwrap_or(&port.to_string()),
|
matches.value_of("port").unwrap_or(&port.to_string()),
|
||||||
matches
|
&key,
|
||||||
.value_of("key")
|
|
||||||
.unwrap_or(&std::env::var("KEY").unwrap_or_default()),
|
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
+13
-5
@@ -15,13 +15,14 @@ fn main() -> ResultType<()> {
|
|||||||
.start()?;
|
.start()?;
|
||||||
let args = format!(
|
let args = format!(
|
||||||
"-c --config=[FILE] +takes_value 'Sets a custom config file'
|
"-c --config=[FILE] +takes_value 'Sets a custom config file'
|
||||||
|
-b, --bind=[IP] 'Sets the IP address to bind to (default: all interfaces)'
|
||||||
-p, --port=[NUMBER(default={RENDEZVOUS_PORT})] 'Sets the listening port'
|
-p, --port=[NUMBER(default={RENDEZVOUS_PORT})] 'Sets the listening port'
|
||||||
-s, --serial=[NUMBER(default=0)] 'Sets configure update serial number'
|
-s, --serial=[NUMBER(default=0)] '[DEPRECATED] Sets configure update serial number'
|
||||||
-R, --rendezvous-servers=[HOSTS] 'Sets rendezvous servers, separated by comma'
|
-R, --rendezvous-servers=[HOSTS] '[DEPRECATED] Sets rendezvous servers, separated by comma'
|
||||||
-u, --software-url=[URL] 'Sets download url of RustDesk software of newest version'
|
-u, --software-url=[URL] '[DEPRECATED] Sets download url of RustDesk software of newest version'
|
||||||
-r, --relay-servers=[HOST] 'Sets the default relay servers, separated by comma'
|
-r, --relay-servers=[HOST] 'Sets the default relay servers, separated by comma'
|
||||||
-M, --rmem=[NUMBER(default={RMEM})] 'Sets UDP recv buffer size, set system rmem_max first, e.g., sudo sysctl -w net.core.rmem_max=52428800. vi /etc/sysctl.conf, net.core.rmem_max=52428800, sudo sysctl –p'
|
-M, --rmem=[NUMBER(default={RMEM})] 'Sets UDP recv buffer size, set system rmem_max first, e.g., sudo sysctl -w net.core.rmem_max=52428800. vi /etc/sysctl.conf, net.core.rmem_max=52428800, sudo sysctl –p'
|
||||||
, --mask=[MASK] 'Determine if the connection comes from LAN, e.g. 192.168.0.0/16'
|
, --mask=[MASK] '[DEPRECATED] Determine if the connection comes from LAN, e.g. 192.168.0.0/16'
|
||||||
-k, --key=[KEY] 'Only allow the client with the same key'
|
-k, --key=[KEY] 'Only allow the client with the same key'
|
||||||
, --must-login=[Y|N] 'Only allow the client with login'",
|
, --must-login=[Y|N] 'Only allow the client with login'",
|
||||||
);
|
);
|
||||||
@@ -30,9 +31,16 @@ fn main() -> ResultType<()> {
|
|||||||
if port < 3 {
|
if port < 3 {
|
||||||
bail!("Invalid port");
|
bail!("Invalid port");
|
||||||
}
|
}
|
||||||
|
let bind_addr = parse_bind_address(&get_arg("bind"))?;
|
||||||
let rmem = get_arg("rmem").parse::<usize>().unwrap_or(RMEM);
|
let rmem = get_arg("rmem").parse::<usize>().unwrap_or(RMEM);
|
||||||
let serial: i32 = get_arg("serial").parse().unwrap_or(0);
|
let serial: i32 = get_arg("serial").parse().unwrap_or(0);
|
||||||
crate::common::check_software_update();
|
crate::common::check_software_update();
|
||||||
RendezvousServer::start(port, serial, &get_arg_or("key", "-".to_owned()), rmem)?;
|
RendezvousServer::start_with_bind(
|
||||||
|
bind_addr,
|
||||||
|
port,
|
||||||
|
serial,
|
||||||
|
&get_arg_or("key", "-".to_owned()),
|
||||||
|
rmem,
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
+1
-1
@@ -67,7 +67,7 @@ pub(crate) struct PeerMap {
|
|||||||
|
|
||||||
impl PeerMap {
|
impl PeerMap {
|
||||||
pub(crate) async fn new() -> ResultType<Self> {
|
pub(crate) async fn new() -> ResultType<Self> {
|
||||||
let db = std::env::var("DB_URL").unwrap_or({
|
let db = get_arg_opt("DB_URL").unwrap_or_else(|| {
|
||||||
let mut db = "db_v2.sqlite3".to_owned();
|
let mut db = "db_v2.sqlite3".to_owned();
|
||||||
#[cfg(all(windows, not(debug_assertions)))]
|
#[cfg(all(windows, not(debug_assertions)))]
|
||||||
{
|
{
|
||||||
|
|||||||
+54
-17
@@ -8,7 +8,7 @@ use hbb_common::{
|
|||||||
protobuf::Message as _,
|
protobuf::Message as _,
|
||||||
rendezvous_proto::*,
|
rendezvous_proto::*,
|
||||||
sleep,
|
sleep,
|
||||||
tcp::{listen_any, FramedStream},
|
tcp::FramedStream,
|
||||||
timeout,
|
timeout,
|
||||||
tokio::{
|
tokio::{
|
||||||
self,
|
self,
|
||||||
@@ -24,7 +24,7 @@ use std::{
|
|||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
io::prelude::*,
|
io::prelude::*,
|
||||||
io::Error,
|
io::Error,
|
||||||
net::SocketAddr,
|
net::{IpAddr, SocketAddr},
|
||||||
sync::atomic::{AtomicUsize, Ordering},
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,14 +39,18 @@ lazy_static::lazy_static! {
|
|||||||
|
|
||||||
static DOWNGRADE_THRESHOLD_100: AtomicUsize = AtomicUsize::new(66); // 0.66
|
static DOWNGRADE_THRESHOLD_100: AtomicUsize = AtomicUsize::new(66); // 0.66
|
||||||
static DOWNGRADE_START_CHECK: AtomicUsize = AtomicUsize::new(1_800_000); // in ms
|
static DOWNGRADE_START_CHECK: AtomicUsize = AtomicUsize::new(1_800_000); // in ms
|
||||||
static LIMIT_SPEED: AtomicUsize = AtomicUsize::new(4 * 1024 * 1024); // in bit/s
|
static LIMIT_SPEED: AtomicUsize = AtomicUsize::new(32 * 1024 * 1024); // in bit/s
|
||||||
static TOTAL_BANDWIDTH: AtomicUsize = AtomicUsize::new(1024 * 1024 * 1024); // in bit/s
|
static TOTAL_BANDWIDTH: AtomicUsize = AtomicUsize::new(1024 * 1024 * 1024); // in bit/s
|
||||||
static SINGLE_BANDWIDTH: AtomicUsize = AtomicUsize::new(16 * 1024 * 1024); // in bit/s
|
static SINGLE_BANDWIDTH: AtomicUsize = AtomicUsize::new(128 * 1024 * 1024); // in bit/s
|
||||||
const BLACKLIST_FILE: &str = "blacklist.txt";
|
const BLACKLIST_FILE: &str = "blacklist.txt";
|
||||||
const BLOCKLIST_FILE: &str = "blocklist.txt";
|
const BLOCKLIST_FILE: &str = "blocklist.txt";
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread")]
|
#[tokio::main(flavor = "multi_thread")]
|
||||||
pub async fn start(port: &str, key: &str) -> ResultType<()> {
|
pub async fn start_with_bind(
|
||||||
|
bind_addr: Option<IpAddr>,
|
||||||
|
port: &str,
|
||||||
|
key: &str,
|
||||||
|
) -> ResultType<()> {
|
||||||
let key = get_server_sk(key);
|
let key = get_server_sk(key);
|
||||||
if let Ok(mut file) = std::fs::File::open(BLACKLIST_FILE) {
|
if let Ok(mut file) = std::fs::File::open(BLACKLIST_FILE) {
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
@@ -85,7 +89,13 @@ pub async fn start(port: &str, key: &str) -> ResultType<()> {
|
|||||||
let main_task = async move {
|
let main_task = async move {
|
||||||
loop {
|
loop {
|
||||||
log::info!("Start");
|
log::info!("Start");
|
||||||
io_loop(listen_any(port).await?, listen_any(port2).await?, &key).await;
|
io_loop(
|
||||||
|
crate::common::listen_tcp(bind_addr, port).await?,
|
||||||
|
crate::common::listen_tcp(bind_addr, port2).await?,
|
||||||
|
crate::common::listen_console(bind_addr, port).await?,
|
||||||
|
&key,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let listen_signal = crate::common::listen_signal();
|
let listen_signal = crate::common::listen_signal();
|
||||||
@@ -96,8 +106,8 @@ pub async fn start(port: &str, key: &str) -> ResultType<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_params() {
|
fn check_params() {
|
||||||
let tmp = std::env::var("DOWNGRADE_THRESHOLD")
|
let tmp = crate::common::get_arg("DOWNGRADE_THRESHOLD")
|
||||||
.map(|x| x.parse::<f64>().unwrap_or(0.))
|
.parse::<f64>()
|
||||||
.unwrap_or(0.);
|
.unwrap_or(0.);
|
||||||
if tmp > 0. {
|
if tmp > 0. {
|
||||||
DOWNGRADE_THRESHOLD_100.store((tmp * 100.) as _, Ordering::SeqCst);
|
DOWNGRADE_THRESHOLD_100.store((tmp * 100.) as _, Ordering::SeqCst);
|
||||||
@@ -106,8 +116,8 @@ fn check_params() {
|
|||||||
"DOWNGRADE_THRESHOLD: {}",
|
"DOWNGRADE_THRESHOLD: {}",
|
||||||
DOWNGRADE_THRESHOLD_100.load(Ordering::SeqCst) as f64 / 100.
|
DOWNGRADE_THRESHOLD_100.load(Ordering::SeqCst) as f64 / 100.
|
||||||
);
|
);
|
||||||
let tmp = std::env::var("DOWNGRADE_START_CHECK")
|
let tmp = crate::common::get_arg("DOWNGRADE_START_CHECK")
|
||||||
.map(|x| x.parse::<usize>().unwrap_or(0))
|
.parse::<usize>()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
if tmp > 0 {
|
if tmp > 0 {
|
||||||
DOWNGRADE_START_CHECK.store(tmp * 1000, Ordering::SeqCst);
|
DOWNGRADE_START_CHECK.store(tmp * 1000, Ordering::SeqCst);
|
||||||
@@ -116,8 +126,8 @@ fn check_params() {
|
|||||||
"DOWNGRADE_START_CHECK: {}s",
|
"DOWNGRADE_START_CHECK: {}s",
|
||||||
DOWNGRADE_START_CHECK.load(Ordering::SeqCst) / 1000
|
DOWNGRADE_START_CHECK.load(Ordering::SeqCst) / 1000
|
||||||
);
|
);
|
||||||
let tmp = std::env::var("LIMIT_SPEED")
|
let tmp = crate::common::get_arg("LIMIT_SPEED")
|
||||||
.map(|x| x.parse::<f64>().unwrap_or(0.))
|
.parse::<f64>()
|
||||||
.unwrap_or(0.);
|
.unwrap_or(0.);
|
||||||
if tmp > 0. {
|
if tmp > 0. {
|
||||||
LIMIT_SPEED.store((tmp * 1024. * 1024.) as usize, Ordering::SeqCst);
|
LIMIT_SPEED.store((tmp * 1024. * 1024.) as usize, Ordering::SeqCst);
|
||||||
@@ -126,8 +136,8 @@ fn check_params() {
|
|||||||
"LIMIT_SPEED: {}Mb/s",
|
"LIMIT_SPEED: {}Mb/s",
|
||||||
LIMIT_SPEED.load(Ordering::SeqCst) as f64 / 1024. / 1024.
|
LIMIT_SPEED.load(Ordering::SeqCst) as f64 / 1024. / 1024.
|
||||||
);
|
);
|
||||||
let tmp = std::env::var("TOTAL_BANDWIDTH")
|
let tmp = crate::common::get_arg("TOTAL_BANDWIDTH")
|
||||||
.map(|x| x.parse::<f64>().unwrap_or(0.))
|
.parse::<f64>()
|
||||||
.unwrap_or(0.);
|
.unwrap_or(0.);
|
||||||
if tmp > 0. {
|
if tmp > 0. {
|
||||||
TOTAL_BANDWIDTH.store((tmp * 1024. * 1024.) as usize, Ordering::SeqCst);
|
TOTAL_BANDWIDTH.store((tmp * 1024. * 1024.) as usize, Ordering::SeqCst);
|
||||||
@@ -137,8 +147,8 @@ fn check_params() {
|
|||||||
"TOTAL_BANDWIDTH: {}Mb/s",
|
"TOTAL_BANDWIDTH: {}Mb/s",
|
||||||
TOTAL_BANDWIDTH.load(Ordering::SeqCst) as f64 / 1024. / 1024.
|
TOTAL_BANDWIDTH.load(Ordering::SeqCst) as f64 / 1024. / 1024.
|
||||||
);
|
);
|
||||||
let tmp = std::env::var("SINGLE_BANDWIDTH")
|
let tmp = crate::common::get_arg("SINGLE_BANDWIDTH")
|
||||||
.map(|x| x.parse::<f64>().unwrap_or(0.))
|
.parse::<f64>()
|
||||||
.unwrap_or(0.);
|
.unwrap_or(0.);
|
||||||
if tmp > 0. {
|
if tmp > 0. {
|
||||||
SINGLE_BANDWIDTH.store((tmp * 1024. * 1024.) as usize, Ordering::SeqCst);
|
SINGLE_BANDWIDTH.store((tmp * 1024. * 1024.) as usize, Ordering::SeqCst);
|
||||||
@@ -323,7 +333,12 @@ async fn check_cmd(cmd: &str, limiter: Limiter) -> String {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn io_loop(listener: TcpListener, listener2: TcpListener, key: &str) {
|
async fn io_loop(
|
||||||
|
listener: TcpListener,
|
||||||
|
listener2: TcpListener,
|
||||||
|
listener_console: Option<TcpListener>,
|
||||||
|
key: &str,
|
||||||
|
) {
|
||||||
check_params();
|
check_params();
|
||||||
let limiter = <Limiter>::new(TOTAL_BANDWIDTH.load(Ordering::SeqCst) as _);
|
let limiter = <Limiter>::new(TOTAL_BANDWIDTH.load(Ordering::SeqCst) as _);
|
||||||
loop {
|
loop {
|
||||||
@@ -352,6 +367,18 @@ async fn io_loop(listener: TcpListener, listener2: TcpListener, key: &str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
res = crate::common::accept_or_pending(listener_console.as_ref()) => {
|
||||||
|
match res {
|
||||||
|
Ok((stream, addr)) => {
|
||||||
|
stream.set_nodelay(true).ok();
|
||||||
|
handle_connection(stream, addr, &limiter, key, false).await;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("console listener.accept failed: {}", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,6 +428,15 @@ async fn make_pair(
|
|||||||
use tokio_tungstenite::tungstenite::handshake::server::{Request, Response};
|
use tokio_tungstenite::tungstenite::handshake::server::{Request, Response};
|
||||||
let callback = |req: &Request, response: Response| {
|
let callback = |req: &Request, response: Response| {
|
||||||
let headers = req.headers();
|
let headers = req.headers();
|
||||||
|
// X-Real-IP / X-Forwarded-For are trusted as-is so that the real
|
||||||
|
// client IP is preserved when the WebSocket port runs behind a
|
||||||
|
// reverse proxy (WSS). They are NOT validated: anyone who can reach
|
||||||
|
// this port directly can spoof an arbitrary IP, bypassing IP-based
|
||||||
|
// rate limiting / blocking and corrupting logged IPs. Do not expose
|
||||||
|
// the WebSocket port directly to untrusted networks; only the
|
||||||
|
// reverse proxy, which overwrites these headers, should be able to
|
||||||
|
// connect to it.
|
||||||
|
// https://github.com/rustdesk/rustdesk-server/issues/634
|
||||||
let real_ip = headers
|
let real_ip = headers
|
||||||
.get("X-Real-IP")
|
.get("X-Real-IP")
|
||||||
.or_else(|| headers.get("X-Forwarded-For"))
|
.or_else(|| headers.get("X-Forwarded-For"))
|
||||||
@@ -428,6 +464,7 @@ async fn make_pair_(stream: impl StreamTrait, addr: SocketAddr, key: &str, limit
|
|||||||
if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) {
|
if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) {
|
||||||
if let Some(rendezvous_message::Union::RequestRelay(rf)) = msg_in.union {
|
if let Some(rendezvous_message::Union::RequestRelay(rf)) = msg_in.union {
|
||||||
if !key.is_empty() && rf.licence_key != key {
|
if !key.is_empty() && rf.licence_key != key {
|
||||||
|
log::warn!("Relay authentication failed from {} - invalid key", addr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if !rf.uuid.is_empty() {
|
if !rf.uuid.is_empty() {
|
||||||
|
|||||||
+133
-38
@@ -23,7 +23,7 @@ use hbb_common::{
|
|||||||
sodiumoxide::hex,
|
sodiumoxide::hex,
|
||||||
tcp,
|
tcp,
|
||||||
tcp::Encrypt,
|
tcp::Encrypt,
|
||||||
tcp::{listen_any, FramedStream},
|
tcp::FramedStream,
|
||||||
timeout,
|
timeout,
|
||||||
tokio::{
|
tokio::{
|
||||||
self,
|
self,
|
||||||
@@ -56,7 +56,7 @@ enum Data {
|
|||||||
RelayServers(RelayServers),
|
RelayServers(RelayServers),
|
||||||
}
|
}
|
||||||
|
|
||||||
const REG_TIMEOUT: i32 = 30_000;
|
const REG_TIMEOUT: i64 = 30_000;
|
||||||
type TcpStreamSink = SplitSink<Framed<TcpStream, BytesCodec>, Bytes>;
|
type TcpStreamSink = SplitSink<Framed<TcpStream, BytesCodec>, Bytes>;
|
||||||
type WsSink = SplitSink<tokio_tungstenite::WebSocketStream<TcpStream>, tungstenite::Message>;
|
type WsSink = SplitSink<tokio_tungstenite::WebSocketStream<TcpStream>, tungstenite::Message>;
|
||||||
struct SafeWsSink {
|
struct SafeWsSink {
|
||||||
@@ -105,6 +105,14 @@ const CHECK_RELAY_TIMEOUT: u64 = 3_000;
|
|||||||
static ALWAYS_USE_RELAY: AtomicBool = AtomicBool::new(false);
|
static ALWAYS_USE_RELAY: AtomicBool = AtomicBool::new(false);
|
||||||
static MUST_LOGIN: AtomicBool = AtomicBool::new(false);
|
static MUST_LOGIN: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
// Store punch hole requests
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use tokio::sync::Mutex as TokioMutex; // differentiate if needed
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct PunchReqEntry { tm: Instant, from_ip: String, to_ip: String, to_id: String }
|
||||||
|
static PUNCH_REQS: Lazy<TokioMutex<Vec<PunchReqEntry>>> = Lazy::new(|| TokioMutex::new(Vec::new()));
|
||||||
|
const PUNCH_REQ_DEDUPE_SEC: u64 = 60;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Inner {
|
struct Inner {
|
||||||
serial: i32,
|
serial: i32,
|
||||||
@@ -134,21 +142,29 @@ enum LoopFailure {
|
|||||||
Listener3,
|
Listener3,
|
||||||
Listener2,
|
Listener2,
|
||||||
Listener,
|
Listener,
|
||||||
|
ConsoleListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RendezvousServer {
|
impl RendezvousServer {
|
||||||
|
pub fn start(port: i32, serial: i32, key: &str, rmem: usize) -> ResultType<()> {
|
||||||
|
Self::start_with_bind(None, port, serial, key, rmem)
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread")]
|
#[tokio::main(flavor = "multi_thread")]
|
||||||
pub async fn start(port: i32, serial: i32, key: &str, rmem: usize) -> ResultType<()> {
|
pub async fn start_with_bind(
|
||||||
|
bind_addr: Option<IpAddr>,
|
||||||
|
port: i32,
|
||||||
|
serial: i32,
|
||||||
|
key: &str,
|
||||||
|
rmem: usize,
|
||||||
|
) -> ResultType<()> {
|
||||||
let (key, sk) = Self::get_server_sk(key);
|
let (key, sk) = Self::get_server_sk(key);
|
||||||
let nat_port = port - 1;
|
let nat_port = port - 1;
|
||||||
let ws_port = port + 2;
|
let ws_port = port + 2;
|
||||||
let pm = PeerMap::new().await?;
|
let pm = PeerMap::new().await?;
|
||||||
log::info!("serial={}", serial);
|
log::info!("serial={}", serial);
|
||||||
let rendezvous_servers = get_servers(&get_arg("rendezvous-servers"), "rendezvous-servers");
|
let rendezvous_servers = get_servers(&get_arg("rendezvous-servers"), "rendezvous-servers");
|
||||||
log::info!("Listening on tcp/udp :{}", port);
|
let mut socket = create_udp_listener(bind_addr, port, rmem).await?;
|
||||||
log::info!("Listening on tcp :{}, extra port for NAT test", nat_port);
|
|
||||||
log::info!("Listening on websocket :{}", ws_port);
|
|
||||||
let mut socket = create_udp_listener(port, rmem).await?;
|
|
||||||
let (tx, mut rx) = mpsc::unbounded_channel::<Data>();
|
let (tx, mut rx) = mpsc::unbounded_channel::<Data>();
|
||||||
let software_url = get_arg("software-url");
|
let software_url = get_arg("software-url");
|
||||||
let version = hbb_common::get_version_from_url(&software_url);
|
let version = hbb_common::get_version_from_url(&software_url);
|
||||||
@@ -191,15 +207,18 @@ impl RendezvousServer {
|
|||||||
log::info!("local-ip: {:?}", rs.inner.local_ip);
|
log::info!("local-ip: {:?}", rs.inner.local_ip);
|
||||||
std::env::set_var("PORT_FOR_API", port.to_string());
|
std::env::set_var("PORT_FOR_API", port.to_string());
|
||||||
rs.parse_relay_servers(&get_arg("relay-servers"));
|
rs.parse_relay_servers(&get_arg("relay-servers"));
|
||||||
let mut listener = create_tcp_listener(port).await?;
|
let mut listener = create_tcp_listener(bind_addr, port).await?;
|
||||||
let mut listener2 = create_tcp_listener(nat_port).await?;
|
let mut listener2 = create_tcp_listener(bind_addr, nat_port).await?;
|
||||||
let mut listener3 = create_tcp_listener(ws_port).await?;
|
let mut listener3 = create_tcp_listener(bind_addr, ws_port).await?;
|
||||||
let test_addr = std::env::var("TEST_HBBS").unwrap_or_default();
|
let mut listener_console = listen_console(bind_addr, nat_port as _).await?;
|
||||||
if std::env::var("ALWAYS_USE_RELAY")
|
log::info!("Listening on tcp/udp {}", listener.local_addr()?);
|
||||||
.unwrap_or_default()
|
log::info!(
|
||||||
.to_uppercase()
|
"Listening on tcp {}, extra port for NAT test",
|
||||||
== "Y"
|
listener2.local_addr()?
|
||||||
{
|
);
|
||||||
|
log::info!("Listening on websocket {}", listener3.local_addr()?);
|
||||||
|
let test_addr = get_arg("TEST_HBBS");
|
||||||
|
if get_arg("ALWAYS_USE_RELAY").to_uppercase() == "Y" {
|
||||||
ALWAYS_USE_RELAY.store(true, Ordering::SeqCst);
|
ALWAYS_USE_RELAY.store(true, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
log::info!(
|
log::info!(
|
||||||
@@ -262,6 +281,7 @@ impl RendezvousServer {
|
|||||||
&mut listener,
|
&mut listener,
|
||||||
&mut listener2,
|
&mut listener2,
|
||||||
&mut listener3,
|
&mut listener3,
|
||||||
|
&mut listener_console,
|
||||||
&mut socket,
|
&mut socket,
|
||||||
&key,
|
&key,
|
||||||
)
|
)
|
||||||
@@ -269,19 +289,23 @@ impl RendezvousServer {
|
|||||||
{
|
{
|
||||||
LoopFailure::UdpSocket => {
|
LoopFailure::UdpSocket => {
|
||||||
drop(socket);
|
drop(socket);
|
||||||
socket = create_udp_listener(port, rmem).await?;
|
socket = create_udp_listener(bind_addr, port, rmem).await?;
|
||||||
}
|
}
|
||||||
LoopFailure::Listener => {
|
LoopFailure::Listener => {
|
||||||
drop(listener);
|
drop(listener);
|
||||||
listener = create_tcp_listener(port).await?;
|
listener = create_tcp_listener(bind_addr, port).await?;
|
||||||
}
|
}
|
||||||
LoopFailure::Listener2 => {
|
LoopFailure::Listener2 => {
|
||||||
drop(listener2);
|
drop(listener2);
|
||||||
listener2 = create_tcp_listener(nat_port).await?;
|
listener2 = create_tcp_listener(bind_addr, nat_port).await?;
|
||||||
|
}
|
||||||
|
LoopFailure::ConsoleListener => {
|
||||||
|
drop(listener_console.take());
|
||||||
|
listener_console = listen_console(bind_addr, nat_port as _).await?;
|
||||||
}
|
}
|
||||||
LoopFailure::Listener3 => {
|
LoopFailure::Listener3 => {
|
||||||
drop(listener3);
|
drop(listener3);
|
||||||
listener3 = create_tcp_listener(ws_port).await?;
|
listener3 = create_tcp_listener(bind_addr, ws_port).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,6 +323,7 @@ impl RendezvousServer {
|
|||||||
listener: &mut TcpListener,
|
listener: &mut TcpListener,
|
||||||
listener2: &mut TcpListener,
|
listener2: &mut TcpListener,
|
||||||
listener3: &mut TcpListener,
|
listener3: &mut TcpListener,
|
||||||
|
listener_console: &mut Option<TcpListener>,
|
||||||
socket: &mut FramedSocket,
|
socket: &mut FramedSocket,
|
||||||
key: &str,
|
key: &str,
|
||||||
) -> LoopFailure {
|
) -> LoopFailure {
|
||||||
@@ -350,6 +375,18 @@ impl RendezvousServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
res = accept_or_pending(listener_console.as_ref()) => {
|
||||||
|
match res {
|
||||||
|
Ok((stream, addr)) => {
|
||||||
|
stream.set_nodelay(true).ok();
|
||||||
|
self.handle_listener2(stream, addr).await;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("console listener.accept failed: {}", err);
|
||||||
|
return LoopFailure::ConsoleListener;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
res = listener3.accept() => {
|
res = listener3.accept() => {
|
||||||
match res {
|
match res {
|
||||||
Ok((stream, addr)) => {
|
Ok((stream, addr)) => {
|
||||||
@@ -432,22 +469,14 @@ impl RendezvousServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(rendezvous_message::Union::PunchHoleRequest(ph)) => {
|
Some(rendezvous_message::Union::PunchHoleRequest(ph)) => {
|
||||||
if self.pm.is_in_memory(&ph.id).await {
|
// UDP PunchHoleRequest is intentionally unsupported.
|
||||||
self.handle_udp_punch_hole_request(addr, ph, key).await?;
|
// The supported client path sends PunchHoleRequest over TCP/WS.
|
||||||
} else {
|
|
||||||
// not in memory, fetch from db with spawn in case blocking me
|
|
||||||
let mut me = self.clone();
|
|
||||||
let key = key.to_owned();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
allow_err!(me.handle_udp_punch_hole_request(addr, ph, &key).await);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Some(rendezvous_message::Union::PunchHoleSent(phs)) => {
|
Some(rendezvous_message::Union::PunchHoleSent(phs)) => {
|
||||||
self.handle_hole_sent(phs, addr, Some(socket)).await?;
|
// UDP PunchHoleSent is intentionally unsupported to avoid UDP reflection/amplification
|
||||||
}
|
}
|
||||||
Some(rendezvous_message::Union::LocalAddr(la)) => {
|
Some(rendezvous_message::Union::LocalAddr(la)) => {
|
||||||
self.handle_local_addr(la, addr, Some(socket)).await?;
|
// UDP LocalAddr is intentionally unsupported to avoid UDP reflection/amplification
|
||||||
}
|
}
|
||||||
Some(rendezvous_message::Union::ConfigureUpdate(mut cu)) => {
|
Some(rendezvous_message::Union::ConfigureUpdate(mut cu)) => {
|
||||||
if try_into_v4(addr).ip().is_loopback() && cu.serial > self.inner.serial {
|
if try_into_v4(addr).ip().is_loopback() && cu.serial > self.inner.serial {
|
||||||
@@ -665,7 +694,7 @@ impl RendezvousServer {
|
|||||||
let mut states = BytesMut::zeroed((peers.len() + 7) / 8);
|
let mut states = BytesMut::zeroed((peers.len() + 7) / 8);
|
||||||
for (i, peer_id) in peers.iter().enumerate() {
|
for (i, peer_id) in peers.iter().enumerate() {
|
||||||
if let Some(peer) = self.pm.get_in_memory(peer_id).await {
|
if let Some(peer) = self.pm.get_in_memory(peer_id).await {
|
||||||
let elapsed = peer.read().await.last_reg_time.elapsed().as_millis() as i32;
|
let elapsed = peer.read().await.last_reg_time.elapsed().as_millis() as i64;
|
||||||
// bytes index from left to right
|
// bytes index from left to right
|
||||||
let states_idx = i / 8;
|
let states_idx = i / 8;
|
||||||
let bit_idx = 7 - i % 8;
|
let bit_idx = 7 - i % 8;
|
||||||
@@ -891,6 +920,7 @@ impl RendezvousServer {
|
|||||||
) -> ResultType<(RendezvousMessage, Option<SocketAddr>)> {
|
) -> ResultType<(RendezvousMessage, Option<SocketAddr>)> {
|
||||||
let mut ph = ph;
|
let mut ph = ph;
|
||||||
if !key.is_empty() && ph.licence_key != key {
|
if !key.is_empty() && ph.licence_key != key {
|
||||||
|
log::warn!("Authentication failed from {} for peer {} - invalid key", addr, ph.id);
|
||||||
let mut msg_out = RendezvousMessage::new();
|
let mut msg_out = RendezvousMessage::new();
|
||||||
msg_out.set_punch_hole_response(PunchHoleResponse {
|
msg_out.set_punch_hole_response(PunchHoleResponse {
|
||||||
failure: punch_hole_response::Failure::LICENSE_MISMATCH.into(),
|
failure: punch_hole_response::Failure::LICENSE_MISMATCH.into(),
|
||||||
@@ -930,7 +960,7 @@ impl RendezvousServer {
|
|||||||
if let Some(peer) = self.pm.get(&id).await {
|
if let Some(peer) = self.pm.get(&id).await {
|
||||||
let (elapsed, peer_addr) = {
|
let (elapsed, peer_addr) = {
|
||||||
let r = peer.read().await;
|
let r = peer.read().await;
|
||||||
(r.last_reg_time.elapsed().as_millis() as i32, r.socket_addr)
|
(r.last_reg_time.elapsed().as_millis() as i64, r.socket_addr)
|
||||||
};
|
};
|
||||||
if elapsed >= REG_TIMEOUT {
|
if elapsed >= REG_TIMEOUT {
|
||||||
let mut msg_out = RendezvousMessage::new();
|
let mut msg_out = RendezvousMessage::new();
|
||||||
@@ -940,6 +970,23 @@ impl RendezvousServer {
|
|||||||
});
|
});
|
||||||
return Ok((msg_out, None));
|
return Ok((msg_out, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// record punch hole request (from addr -> peer id/peer_addr)
|
||||||
|
{
|
||||||
|
let from_ip = try_into_v4(addr).ip().to_string();
|
||||||
|
let to_ip = try_into_v4(peer_addr).ip().to_string();
|
||||||
|
let to_id_clone = id.clone();
|
||||||
|
let mut lock = PUNCH_REQS.lock().await;
|
||||||
|
let mut dup = false;
|
||||||
|
for e in lock.iter().rev().take(30) { // only check recent tail subset for speed
|
||||||
|
if e.from_ip == from_ip && e.to_id == to_id_clone {
|
||||||
|
if e.tm.elapsed().as_secs() < PUNCH_REQ_DEDUPE_SEC { dup = true; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !dup { lock.push(PunchReqEntry { tm: Instant::now(), from_ip, to_ip, to_id: to_id_clone }); }
|
||||||
|
}
|
||||||
|
|
||||||
let mut msg_out = RendezvousMessage::new();
|
let mut msg_out = RendezvousMessage::new();
|
||||||
let peer_is_lan = self.is_lan(peer_addr);
|
let peer_is_lan = self.is_lan(peer_addr);
|
||||||
let is_lan = self.is_lan(addr);
|
let is_lan = self.is_lan(addr);
|
||||||
@@ -1136,11 +1183,12 @@ impl RendezvousServer {
|
|||||||
match fds.next() {
|
match fds.next() {
|
||||||
Some("h") => {
|
Some("h") => {
|
||||||
res = format!(
|
res = format!(
|
||||||
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n",
|
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n",
|
||||||
"relay-servers(rs) <separated by ,>",
|
"relay-servers(rs) <separated by ,>",
|
||||||
"reload-geo(rg)",
|
"reload-geo(rg)",
|
||||||
"ip-blocker(ib) [<ip>|<number>] [-]",
|
"ip-blocker(ib) [<ip>|<number>] [-]",
|
||||||
"ip-changes(ic) [<id>|<number>] [-]",
|
"ip-changes(ic) [<id>|<number>] [-]",
|
||||||
|
"punch-requests(pr) [<number>] [-]",
|
||||||
"always-use-relay(aur) [Y|N]",
|
"always-use-relay(aur) [Y|N]",
|
||||||
"test-geo(tg) <ip1> <ip2>",
|
"test-geo(tg) <ip1> <ip2>",
|
||||||
"must-login(ml) [Y|N]",
|
"must-login(ml) [Y|N]",
|
||||||
@@ -1241,6 +1289,24 @@ impl RendezvousServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some("punch-requests" | "pr") => {
|
||||||
|
use std::fmt::Write as _;
|
||||||
|
let mut lock = PUNCH_REQS.lock().await;
|
||||||
|
let arg = fds.next();
|
||||||
|
if let Some("-") = arg { lock.clear(); }
|
||||||
|
else {
|
||||||
|
let mut start = arg.and_then(|x| x.parse::<usize>().ok()).unwrap_or(0);
|
||||||
|
let mut page_size = fds.next().and_then(|x| x.parse::<usize>().ok()).unwrap_or(10);
|
||||||
|
if page_size == 0 { page_size = 10; }
|
||||||
|
for (_, e) in lock.iter().enumerate().skip(start).take(page_size) {
|
||||||
|
let age = e.tm.elapsed();
|
||||||
|
let event_system = std::time::SystemTime::now() - age;
|
||||||
|
let event_iso = chrono::DateTime::<chrono::Utc>::from(event_system)
|
||||||
|
.to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
|
||||||
|
let _ = writeln!(res, "{} {} -> {}@{}", event_iso, e.from_ip, e.to_id, e.to_ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some("always-use-relay" | "aur") => {
|
Some("always-use-relay" | "aur") => {
|
||||||
if let Some(rs) = fds.next() {
|
if let Some(rs) = fds.next() {
|
||||||
if rs.to_uppercase() == "Y" {
|
if rs.to_uppercase() == "Y" {
|
||||||
@@ -1348,6 +1414,15 @@ impl RendezvousServer {
|
|||||||
use tokio_tungstenite::tungstenite::handshake::server::{Request, Response};
|
use tokio_tungstenite::tungstenite::handshake::server::{Request, Response};
|
||||||
let callback = |req: &Request, response: Response| {
|
let callback = |req: &Request, response: Response| {
|
||||||
let headers = req.headers();
|
let headers = req.headers();
|
||||||
|
// X-Real-IP / X-Forwarded-For are trusted as-is so that the real
|
||||||
|
// client IP is preserved when the WebSocket port runs behind a
|
||||||
|
// reverse proxy (WSS). They are NOT validated: anyone who can reach
|
||||||
|
// this port directly can spoof an arbitrary IP, bypassing IP-based
|
||||||
|
// rate limiting / blocking and corrupting logged IPs. Do not expose
|
||||||
|
// the WebSocket port directly to untrusted networks; only the
|
||||||
|
// reverse proxy, which overwrites these headers, should be able to
|
||||||
|
// connect to it.
|
||||||
|
// https://github.com/rustdesk/rustdesk-server/issues/634
|
||||||
let real_ip = headers
|
let real_ip = headers
|
||||||
.get("X-Real-IP")
|
.get("X-Real-IP")
|
||||||
.or_else(|| headers.get("X-Forwarded-For"))
|
.or_else(|| headers.get("X-Forwarded-For"))
|
||||||
@@ -1582,7 +1657,15 @@ async fn send_rk_res(
|
|||||||
socket.send(&msg_out, addr).await
|
socket.send(&msg_out, addr).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_udp_listener(port: i32, rmem: usize) -> ResultType<FramedSocket> {
|
async fn create_udp_listener(
|
||||||
|
bind_addr: Option<IpAddr>,
|
||||||
|
port: i32,
|
||||||
|
rmem: usize,
|
||||||
|
) -> ResultType<FramedSocket> {
|
||||||
|
if let Some(bind_addr) = bind_addr {
|
||||||
|
let addr = SocketAddr::new(bind_addr, port as _);
|
||||||
|
return FramedSocket::new_reuse(&addr, true, rmem).await;
|
||||||
|
}
|
||||||
let addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port as _);
|
let addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port as _);
|
||||||
if let Ok(s) = FramedSocket::new_reuse(&addr, true, rmem).await {
|
if let Ok(s) = FramedSocket::new_reuse(&addr, true, rmem).await {
|
||||||
log::debug!("listen on udp {:?}", s.local_addr());
|
log::debug!("listen on udp {:?}", s.local_addr());
|
||||||
@@ -1595,8 +1678,8 @@ async fn create_udp_listener(port: i32, rmem: usize) -> ResultType<FramedSocket>
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
async fn create_tcp_listener(port: i32) -> ResultType<TcpListener> {
|
async fn create_tcp_listener(bind_addr: Option<IpAddr>, port: i32) -> ResultType<TcpListener> {
|
||||||
let s = listen_any(port as _).await?;
|
let s = listen_tcp(bind_addr, port as _).await?;
|
||||||
log::debug!("listen on tcp {:?}", s.local_addr());
|
log::debug!("listen on tcp {:?}", s.local_addr());
|
||||||
Ok(s)
|
Ok(s)
|
||||||
}
|
}
|
||||||
@@ -1619,3 +1702,15 @@ fn get_symetric_key_from_msg(
|
|||||||
Err(e) => panic!("Error while opening the seal key{:?}", e),
|
Err(e) => panic!("Error while opening the seal key{:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[hbb_common::tokio::test]
|
||||||
|
async fn udp_listener_uses_bind_address() {
|
||||||
|
let bind_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
|
||||||
|
let socket = create_udp_listener(Some(bind_addr), 0, 0).await.unwrap();
|
||||||
|
assert_eq!(socket.local_addr().unwrap().ip(), bind_addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -10,7 +10,7 @@ use std::{
|
|||||||
fn print_help() {
|
fn print_help() {
|
||||||
println!(
|
println!(
|
||||||
"Usage:
|
"Usage:
|
||||||
rustdesk-util [command]\n
|
rustdesk-utils [command]\n
|
||||||
Available Commands:
|
Available Commands:
|
||||||
genkeypair Generate a new keypair
|
genkeypair Generate a new keypair
|
||||||
validatekeypair [public key] [secret key] Validate an existing keypair
|
validatekeypair [public key] [secret key] Validate an existing keypair
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
use hbb_common::{protobuf::Message, rendezvous_proto::RendezvousMessage};
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
const CHILD_ENV: &str = "RUSTDESK_PROTOBUF_RECURSION_CHILD";
|
||||||
|
const CHILD_COMPLETION_SENTINEL: &str = "protobuf_recursion_child_completed";
|
||||||
|
const TEST_NAME: &str = "deeply_nested_unknown_groups_are_rejected_without_aborting";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deeply_nested_unknown_groups_are_rejected_without_aborting() {
|
||||||
|
if std::env::var_os(CHILD_ENV).is_some() {
|
||||||
|
const DEPTH: usize = 300_000;
|
||||||
|
let mut input = vec![0x0b; DEPTH];
|
||||||
|
input.extend(std::iter::repeat(0x0c).take(DEPTH));
|
||||||
|
|
||||||
|
assert!(RendezvousMessage::parse_from_bytes(&input).is_err());
|
||||||
|
println!("{CHILD_COMPLETION_SENTINEL}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = Command::new(std::env::current_exe().expect("locate the test binary"))
|
||||||
|
.args(["--exact", TEST_NAME, "--nocapture"])
|
||||||
|
.env(CHILD_ENV, "1")
|
||||||
|
.output()
|
||||||
|
.expect("run the recursive-input check in an isolated process");
|
||||||
|
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
assert!(
|
||||||
|
output.status.success() && stdout.lines().any(|line| line == CHILD_COMPLETION_SENTINEL),
|
||||||
|
"child parser check did not complete successfully (status: {}):\nstdout:\n{}\nstderr:\n{}",
|
||||||
|
output.status,
|
||||||
|
stdout,
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
);
|
||||||
|
}
|
||||||
Generated
+77
-11
@@ -1,6 +1,6 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler"
|
name = "adler"
|
||||||
@@ -1507,9 +1507,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.139"
|
version = "0.2.181"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
@@ -1635,14 +1635,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.8.5"
|
version = "0.8.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
|
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.42.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3568,12 +3568,12 @@ version = "0.42.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm 0.42.1",
|
||||||
"windows_aarch64_msvc 0.42.1",
|
"windows_aarch64_msvc 0.42.1",
|
||||||
"windows_i686_gnu 0.42.1",
|
"windows_i686_gnu 0.42.1",
|
||||||
"windows_i686_msvc 0.42.1",
|
"windows_i686_msvc 0.42.1",
|
||||||
"windows_x86_64_gnu 0.42.1",
|
"windows_x86_64_gnu 0.42.1",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm 0.42.1",
|
||||||
"windows_x86_64_msvc 0.42.1",
|
"windows_x86_64_msvc 0.42.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3583,7 +3583,16 @@ version = "0.45.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets",
|
"windows-targets 0.42.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3592,15 +3601,30 @@ version = "0.42.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
|
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm 0.42.1",
|
||||||
"windows_aarch64_msvc 0.42.1",
|
"windows_aarch64_msvc 0.42.1",
|
||||||
"windows_i686_gnu 0.42.1",
|
"windows_i686_gnu 0.42.1",
|
||||||
"windows_i686_msvc 0.42.1",
|
"windows_i686_msvc 0.42.1",
|
||||||
"windows_x86_64_gnu 0.42.1",
|
"windows_x86_64_gnu 0.42.1",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm 0.42.1",
|
||||||
"windows_x86_64_msvc 0.42.1",
|
"windows_x86_64_msvc 0.42.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.48.5",
|
||||||
|
"windows_aarch64_msvc 0.48.5",
|
||||||
|
"windows_i686_gnu 0.48.5",
|
||||||
|
"windows_i686_msvc 0.48.5",
|
||||||
|
"windows_x86_64_gnu 0.48.5",
|
||||||
|
"windows_x86_64_gnullvm 0.48.5",
|
||||||
|
"windows_x86_64_msvc 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-tokens"
|
name = "windows-tokens"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
@@ -3613,6 +3637,12 @@ version = "0.42.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
@@ -3631,6 +3661,12 @@ version = "0.42.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
@@ -3649,6 +3685,12 @@ version = "0.42.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
@@ -3667,6 +3709,12 @@ version = "0.42.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
@@ -3685,12 +3733,24 @@ version = "0.42.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.42.1"
|
version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
@@ -3709,6 +3769,12 @@ version = "0.42.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winres"
|
name = "winres"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
!define PRODUCT_NAME "rustdesk_server"
|
!define PRODUCT_NAME "rustdesk_server"
|
||||||
!define PRODUCT_DESCRIPTION "Installer for ${PRODUCT_NAME}"
|
!define PRODUCT_DESCRIPTION "Installer for ${PRODUCT_NAME}"
|
||||||
!define COPYRIGHT "Copyright © 2021"
|
!define COPYRIGHT "Copyright © 2021"
|
||||||
!define VERSION "1.1.14"
|
!define VERSION "1.1.17"
|
||||||
|
|
||||||
VIProductVersion "${VERSION}.0"
|
VIProductVersion "${VERSION}.0"
|
||||||
VIAddVersionKey "ProductName" "${PRODUCT_NAME}"
|
VIAddVersionKey "ProductName" "${PRODUCT_NAME}"
|
||||||
|
|||||||
Reference in new issue
Block a user