Просмотр исходного кода

Merge branch 'master' into tmp

rustdesk лет назад: 3
Родитель
Сommit
a974906fdc

+ 5 - 5
.github/workflows/build.yaml

@@ -26,7 +26,7 @@ jobs:
26 26
   build:
27 27
 
28 28
     name: Build - ${{ matrix.job.name }}
29
-    runs-on: ubuntu-22.04
29
+    runs-on: ubuntu-18.04
30 30
     strategy:
31 31
       fail-fast: false
32 32
       matrix:
@@ -114,7 +114,7 @@ jobs:
114 114
     needs: 
115 115
       - build
116 116
       - build-win
117
-    runs-on: ubuntu-22.04
117
+    runs-on: ubuntu-18.04
118 118
     strategy:
119 119
       fail-fast: false
120 120
       matrix:
@@ -153,7 +153,7 @@ jobs:
153 153
 
154 154
     name: Docker push - ${{ matrix.job.name }}
155 155
     needs: build
156
-    runs-on: ubuntu-22.04
156
+    runs-on: ubuntu-18.04
157 157
     strategy:
158 158
       fail-fast: false
159 159
       matrix:
@@ -223,7 +223,7 @@ jobs:
223 223
 
224 224
     name: Docker manifest
225 225
     needs: docker
226
-    runs-on: ubuntu-22.04
226
+    runs-on: ubuntu-18.04
227 227
 
228 228
     steps:
229 229
 
@@ -275,7 +275,7 @@ jobs:
275 275
 
276 276
     name: Docker push classic - ${{ matrix.job.name }}
277 277
     needs: build
278
-    runs-on: ubuntu-22.04
278
+    runs-on: ubuntu-18.04
279 279
     strategy:
280 280
       fail-fast: false
281 281
       matrix:

+ 72 - 0
.github/workflows/test.yml

@@ -0,0 +1,72 @@
1
+name: test
2
+
3
+on:
4
+  push:
5
+    branches: [ "master" ]
6
+  pull_request:
7
+    branches: [ "master" ]
8
+
9
+jobs:
10
+  check:
11
+    runs-on: ubuntu-latest
12
+    steps:
13
+      - uses: actions/checkout@v3
14
+      - uses: actions-rs/toolchain@v1
15
+        with:
16
+          profile: minimal
17
+          toolchain: stable
18
+          override: true
19
+      - uses: Swatinem/rust-cache@v2
20
+      - uses: actions-rs/cargo@v1
21
+        with:
22
+          command: check
23
+
24
+  test:
25
+    runs-on: ubuntu-latest
26
+    steps:
27
+      - uses: actions/checkout@v3
28
+      - uses: actions-rs/toolchain@v1
29
+        with:
30
+          profile: minimal
31
+          toolchain: stable
32
+          override: true
33
+      - uses: Swatinem/rust-cache@v2
34
+      - uses: actions-rs/cargo@v1
35
+        with:
36
+          command: test
37
+          args: --all
38
+
39
+  fmt:
40
+    runs-on: ubuntu-latest
41
+    steps:
42
+      - uses: actions/checkout@v3
43
+      - uses: actions-rs/toolchain@v1
44
+        with:
45
+          profile: minimal
46
+          toolchain: stable
47
+          override: true
48
+          components: rustfmt
49
+      - uses: Swatinem/rust-cache@v2
50
+      - uses: actions-rs/cargo@v1
51
+        with:
52
+          command: build
53
+      - uses: actions-rs/cargo@v1
54
+        with:
55
+          command: fmt
56
+          args: --all -- --check
57
+
58
+  clippy:
59
+    runs-on: ubuntu-latest
60
+    steps:
61
+      - uses: actions/checkout@v3
62
+      - uses: actions-rs/toolchain@v1
63
+        with:
64
+          profile: minimal
65
+          toolchain: stable
66
+          override: true
67
+          components: clippy
68
+      - uses: Swatinem/rust-cache@v2
69
+      - uses: actions-rs/cargo@v1
70
+        with:
71
+          command: clippy
72
+          args: --all -- -D warnings

+ 2 - 0
.gitignore

@@ -6,3 +6,5 @@ debian/.debhelper
6 6
 debian/debhelper-build-stamp
7 7
 .DS_Store
8 8
 .vscode
9
+src/version.rs
10
+db_v2.sqlite3

+ 1 - 1
Cargo.lock

@@ -785,7 +785,7 @@ dependencies = [
785 785
 
786 786
 [[package]]
787 787
 name = "hbbs"
788
-version = "1.1.6"
788
+version = "1.1.7"
789 789
 dependencies = [
790 790
  "async-speed-limit",
791 791
  "async-trait",

+ 1 - 1
Cargo.toml

@@ -1,6 +1,6 @@
1 1
 [package]
2 2
 name = "hbbs"
3
-version = "1.1.6"
3
+version = "1.1.7"
4 4
 authors = ["open-trade <info@rustdesk.com>"]
5 5
 edition = "2021"
6 6
 build = "build.rs"

+ 20 - 3
README.md

@@ -173,16 +173,14 @@ services:
173 173
     restart: unless-stopped
174 174
 ```
175 175
 
176
-We use these environment variables:
176
+For this container image, you can use these environment variables, **in addition** to the ones specified in the following **ENV variables** section:
177 177
 
178 178
 | variable | optional | description |
179 179
 | --- | --- | --- |
180 180
 | RELAY | no | the IP address/DNS name of the machine running this container |
181 181
 | ENCRYPTED_ONLY | yes | if set to **"1"** unencrypted connection will not be accepted |
182
-| DB_URL | yes | path for database file |
183 182
 | KEY_PUB | yes | public part of the key pair |
184 183
 | KEY_PRIV | yes | private part of the key pair |
185
-| RUST_LOG | yes | set debug level (error|warn|info|debug|trace) |
186 184
 
187 185
 ### Secret management in S6-overlay based images
188 186
 
@@ -316,3 +314,22 @@ These packages are meant for the following distributions:
316 314
 - Ubuntu 18.04 LTS
317 315
 - Debian 11 bullseye
318 316
 - Debian 10 buster
317
+
318
+## ENV variables
319
+
320
+hbbs and hbbr can be configured using these ENV variables.
321
+You can specify the variables as usual or use an `.env` file.
322
+
323
+| variable | binary | description |
324
+| --- | --- | --- |
325
+| ALWAYS_USE_RELAY | hbbs | if set to **"Y"** disallows direct peer connection |
326
+| DB_URL | hbbs | path for database file |
327
+| DOWNGRADE_START_CHECK | hbbr | delay (in seconds) before downgrade check |
328
+| DOWNGRADE_THRESHOLD | hbbr | threshold of downgrade check (bit/ms) |
329
+| KEY | hbbs/hbbr | if set force the use of a specific key, if set to **"_"** force the use of any key |
330
+| LIMIT_SPEED | hbbr | speed limit (in Mb/s) |
331
+| PORT | hbbs/hbbr | listening port (21116 for hbbs - 21117 for hbbr) |
332
+| RELAY_SERVERS | hbbs | IP address/DNS name of the machines running hbbr (separated by comma) |
333
+| RUST_LOG | all | set debug level (error\|warn\|info\|debug\|trace) |
334
+| SINGLE_BANDWIDTH | hbbr | max bandwidth for a single connection (in Mb/s) |
335
+| TOTAL_BANDWIDTH | hbbr | max total bandwidth (in Mb/s) |


+ 6 - 0
debian/changelog

@@ -1,3 +1,9 @@
1
+rustdesk-server (1.1.7) UNRELEASED; urgency=medium
2
+
3
+  * ipv6 support
4
+
5
+ -- rustdesk <info@rustdesk.com>  Wed, 11 Jan 2023 11:27:00 +0800
6
+
1 7
 rustdesk-server (1.1.6) UNRELEASED; urgency=medium
2 8
 
3 9
   * Initial release

+ 4 - 0
debian/rustdesk-server-hbbr.postinst

@@ -3,6 +3,10 @@ set -e
3 3
 
4 4
 SERVICE=rustdesk-hbbr.service
5 5
 
6
+if [ "$1" = "configure" ]; then
7
+    mkdir -p /var/log/rustdesk
8
+fi
9
+
6 10
 case "$1" in
7 11
     configure|abort-upgrade|abort-deconfigure|abort-remove)
8 12
       mkdir -p /var/lib/rustdesk-server/

+ 1 - 1
debian/rustdesk-server-hbbr.postrm

@@ -6,7 +6,7 @@ SERVICE=rustdesk-hbbr.service
6 6
 systemctl --system daemon-reload >/dev/null || true
7 7
 
8 8
 if [ "$1" = "purge" ]; then
9
-	rm -rf /var/lib/rustdesk-server/
9
+	rm -rf /var/lib/rustdesk-server/ /var/log/rustdesk/rustdesk-hbbr.* /var/log/rustdesk/rustdesk-hbbs.*
10 10
 	deb-systemd-helper purge "${SERVICE}" >/dev/null || true
11 11
 	deb-systemd-helper unmask "${SERVICE}" >/dev/null || true
12 12
 fi

+ 4 - 0
debian/rustdesk-server-hbbs.postinst

@@ -3,6 +3,10 @@ set -e
3 3
 
4 4
 SERVICE=rustdesk-hbbs.service
5 5
 
6
+if [ "$1" = "configure" ]; then
7
+    mkdir -p /var/log/rustdesk
8
+fi
9
+
6 10
 case "$1" in
7 11
     configure|abort-upgrade|abort-deconfigure|abort-remove)
8 12
       mkdir -p /var/lib/rustdesk-server/

+ 2 - 2
docker/rootfs/etc/s6-overlay/s6-rc.d/hbbr/run

@@ -1,3 +1,3 @@
1
-#!/command/execlineb -P
2
-posix-cd /data
1
+#!/command/with-contenv sh
2
+cd /data
3 3
 /usr/bin/hbbr

+ 1 - 4
libs/hbb_common/build.rs

@@ -8,10 +8,7 @@ fn main() {
8 8
         .out_dir(out_dir)
9 9
         .inputs(&["protos/rendezvous.proto", "protos/message.proto"])
10 10
         .include("protos")
11
-        .customize(
12
-            protobuf_codegen::Customize::default()
13
-            .tokio_bytes(true)
14
-        )
11
+        .customize(protobuf_codegen::Customize::default().tokio_bytes(true))
15 12
         .run()
16 13
         .expect("Codegen failed.");
17 14
 }

+ 7 - 1
libs/hbb_common/src/bytes_codec.rs

@@ -15,6 +15,12 @@ enum DecodeState {
15 15
     Data(usize),
16 16
 }
17 17
 
18
+impl Default for BytesCodec {
19
+    fn default() -> Self {
20
+        Self::new()
21
+    }
22
+}
23
+
18 24
 impl BytesCodec {
19 25
     pub fn new() -> Self {
20 26
         Self {
@@ -56,7 +62,7 @@ impl BytesCodec {
56 62
         }
57 63
         src.advance(head_len);
58 64
         src.reserve(n);
59
-        return Ok(Some(n));
65
+        Ok(Some(n))
60 66
     }
61 67
 
62 68
     fn decode_data(&self, n: usize, src: &mut BytesMut) -> io::Result<Option<BytesMut>> {

+ 1 - 6
libs/hbb_common/src/compress.rs

@@ -32,12 +32,7 @@ pub fn decompress(data: &[u8]) -> Vec<u8> {
32 32
             const MAX: usize = 1024 * 1024 * 64;
33 33
             const MIN: usize = 1024 * 1024;
34 34
             let mut n = 30 * data.len();
35
-            if n > MAX {
36
-                n = MAX;
37
-            }
38
-            if n < MIN {
39
-                n = MIN;
40
-            }
35
+            n = n.clamp(MIN, MAX);
41 36
             match d.decompress(data, n) {
42 37
                 Ok(res) => out = res,
43 38
                 Err(err) => {

Разница между файлами не показана из-за своего большого размера
+ 56 - 70
libs/hbb_common/src/config.rs


+ 85 - 96
libs/hbb_common/src/fs.rs

@@ -13,13 +13,13 @@ use crate::{
13 13
     config::{Config, COMPRESS_LEVEL},
14 14
 };
15 15
 
16
-pub fn read_dir(path: &PathBuf, include_hidden: bool) -> ResultType<FileDirectory> {
16
+pub fn read_dir(path: &Path, include_hidden: bool) -> ResultType<FileDirectory> {
17 17
     let mut dir = FileDirectory {
18
-        path: get_string(&path),
18
+        path: get_string(path),
19 19
         ..Default::default()
20 20
     };
21 21
     #[cfg(windows)]
22
-    if "/" == &get_string(&path) {
22
+    if "/" == &get_string(path) {
23 23
         let drives = unsafe { winapi::um::fileapi::GetLogicalDrives() };
24 24
         for i in 0..32 {
25 25
             if drives & (1 << i) != 0 {
@@ -36,74 +36,70 @@ pub fn read_dir(path: &PathBuf, include_hidden: bool) -> ResultType<FileDirector
36 36
         }
37 37
         return Ok(dir);
38 38
     }
39
-    for entry in path.read_dir()? {
40
-        if let Ok(entry) = entry {
41
-            let p = entry.path();
42
-            let name = p
43
-                .file_name()
44
-                .map(|p| p.to_str().unwrap_or(""))
45
-                .unwrap_or("")
46
-                .to_owned();
47
-            if name.is_empty() {
48
-                continue;
49
-            }
50
-            let mut is_hidden = false;
51
-            let meta;
52
-            if let Ok(tmp) = std::fs::symlink_metadata(&p) {
53
-                meta = tmp;
54
-            } else {
55
-                continue;
56
-            }
57
-            // docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
58
-            #[cfg(windows)]
59
-            if meta.file_attributes() & 0x2 != 0 {
60
-                is_hidden = true;
61
-            }
62
-            #[cfg(not(windows))]
63
-            if name.find('.').unwrap_or(usize::MAX) == 0 {
64
-                is_hidden = true;
65
-            }
66
-            if is_hidden && !include_hidden {
67
-                continue;
68
-            }
69
-            let (entry_type, size) = {
70
-                if p.is_dir() {
71
-                    if meta.file_type().is_symlink() {
72
-                        (FileType::DirLink.into(), 0)
73
-                    } else {
74
-                        (FileType::Dir.into(), 0)
75
-                    }
39
+    for entry in path.read_dir()?.flatten() {
40
+        let p = entry.path();
41
+        let name = p
42
+            .file_name()
43
+            .map(|p| p.to_str().unwrap_or(""))
44
+            .unwrap_or("")
45
+            .to_owned();
46
+        if name.is_empty() {
47
+            continue;
48
+        }
49
+        let mut is_hidden = false;
50
+        let meta;
51
+        if let Ok(tmp) = std::fs::symlink_metadata(&p) {
52
+            meta = tmp;
53
+        } else {
54
+            continue;
55
+        }
56
+        // docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
57
+        #[cfg(windows)]
58
+        if meta.file_attributes() & 0x2 != 0 {
59
+            is_hidden = true;
60
+        }
61
+        #[cfg(not(windows))]
62
+        if name.find('.').unwrap_or(usize::MAX) == 0 {
63
+            is_hidden = true;
64
+        }
65
+        if is_hidden && !include_hidden {
66
+            continue;
67
+        }
68
+        let (entry_type, size) = {
69
+            if p.is_dir() {
70
+                if meta.file_type().is_symlink() {
71
+                    (FileType::DirLink.into(), 0)
76 72
                 } else {
77
-                    if meta.file_type().is_symlink() {
78
-                        (FileType::FileLink.into(), 0)
79
-                    } else {
80
-                        (FileType::File.into(), meta.len())
81
-                    }
73
+                    (FileType::Dir.into(), 0)
82 74
                 }
83
-            };
84
-            let modified_time = meta
85
-                .modified()
86
-                .map(|x| {
87
-                    x.duration_since(std::time::SystemTime::UNIX_EPOCH)
88
-                        .map(|x| x.as_secs())
89
-                        .unwrap_or(0)
90
-                })
91
-                .unwrap_or(0) as u64;
92
-            dir.entries.push(FileEntry {
93
-                name: get_file_name(&p),
94
-                entry_type,
95
-                is_hidden,
96
-                size,
97
-                modified_time,
98
-                ..Default::default()
99
-            });
100
-        }
75
+            } else if meta.file_type().is_symlink() {
76
+                (FileType::FileLink.into(), 0)
77
+            } else {
78
+                (FileType::File.into(), meta.len())
79
+            }
80
+        };
81
+        let modified_time = meta
82
+            .modified()
83
+            .map(|x| {
84
+                x.duration_since(std::time::SystemTime::UNIX_EPOCH)
85
+                    .map(|x| x.as_secs())
86
+                    .unwrap_or(0)
87
+            })
88
+            .unwrap_or(0);
89
+        dir.entries.push(FileEntry {
90
+            name: get_file_name(&p),
91
+            entry_type,
92
+            is_hidden,
93
+            size,
94
+            modified_time,
95
+            ..Default::default()
96
+        });
101 97
     }
102 98
     Ok(dir)
103 99
 }
104 100
 
105 101
 #[inline]
106
-pub fn get_file_name(p: &PathBuf) -> String {
102
+pub fn get_file_name(p: &Path) -> String {
107 103
     p.file_name()
108 104
         .map(|p| p.to_str().unwrap_or(""))
109 105
         .unwrap_or("")
@@ -111,7 +107,7 @@ pub fn get_file_name(p: &PathBuf) -> String {
111 107
 }
112 108
 
113 109
 #[inline]
114
-pub fn get_string(path: &PathBuf) -> String {
110
+pub fn get_string(path: &Path) -> String {
115 111
     path.to_str().unwrap_or("").to_owned()
116 112
 }
117 113
 
@@ -127,14 +123,14 @@ pub fn get_home_as_string() -> String {
127 123
 
128 124
 fn read_dir_recursive(
129 125
     path: &PathBuf,
130
-    prefix: &PathBuf,
126
+    prefix: &Path,
131 127
     include_hidden: bool,
132 128
 ) -> ResultType<Vec<FileEntry>> {
133 129
     let mut files = Vec::new();
134 130
     if path.is_dir() {
135 131
         // to-do: symbol link handling, cp the link rather than the content
136 132
         // to-do: file mode, for unix
137
-        let fd = read_dir(&path, include_hidden)?;
133
+        let fd = read_dir(path, include_hidden)?;
138 134
         for entry in fd.entries.iter() {
139 135
             match entry.entry_type.enum_value() {
140 136
                 Ok(FileType::File) => {
@@ -158,7 +154,7 @@ fn read_dir_recursive(
158 154
         }
159 155
         Ok(files)
160 156
     } else if path.is_file() {
161
-        let (size, modified_time) = if let Ok(meta) = std::fs::metadata(&path) {
157
+        let (size, modified_time) = if let Ok(meta) = std::fs::metadata(path) {
162 158
             (
163 159
                 meta.len(),
164 160
                 meta.modified()
@@ -167,7 +163,7 @@ fn read_dir_recursive(
167 163
                             .map(|x| x.as_secs())
168 164
                             .unwrap_or(0)
169 165
                     })
170
-                    .unwrap_or(0) as u64,
166
+                    .unwrap_or(0),
171 167
             )
172 168
         } else {
173 169
             (0, 0)
@@ -249,7 +245,7 @@ pub struct RemoveJobMeta {
249 245
 
250 246
 #[inline]
251 247
 fn get_ext(name: &str) -> &str {
252
-    if let Some(i) = name.rfind(".") {
248
+    if let Some(i) = name.rfind('.') {
253 249
         return &name[i + 1..];
254 250
     }
255 251
     ""
@@ -270,6 +266,7 @@ fn is_compressed_file(name: &str) -> bool {
270 266
 }
271 267
 
272 268
 impl TransferJob {
269
+    #[allow(clippy::too_many_arguments)]
273 270
     pub fn new_write(
274 271
         id: i32,
275 272
         remote: String,
@@ -281,7 +278,7 @@ impl TransferJob {
281 278
         enable_overwrite_detection: bool,
282 279
     ) -> Self {
283 280
         log::info!("new write {}", path);
284
-        let total_size = files.iter().map(|x| x.size as u64).sum();
281
+        let total_size = files.iter().map(|x| x.size).sum();
285 282
         Self {
286 283
             id,
287 284
             remote,
@@ -307,7 +304,7 @@ impl TransferJob {
307 304
     ) -> ResultType<Self> {
308 305
         log::info!("new read {}", path);
309 306
         let files = get_recursive_files(&path, show_hidden)?;
310
-        let total_size = files.iter().map(|x| x.size as u64).sum();
307
+        let total_size = files.iter().map(|x| x.size).sum();
311 308
         Ok(Self {
312 309
             id,
313 310
             remote,
@@ -363,7 +360,7 @@ impl TransferJob {
363 360
             let entry = &self.files[file_num];
364 361
             let path = self.join(&entry.name);
365 362
             let download_path = format!("{}.download", get_string(&path));
366
-            std::fs::rename(&download_path, &path).ok();
363
+            std::fs::rename(download_path, &path).ok();
367 364
             filetime::set_file_mtime(
368 365
                 &path,
369 366
                 filetime::FileTime::from_unix_time(entry.modified_time as _, 0),
@@ -378,7 +375,7 @@ impl TransferJob {
378 375
             let entry = &self.files[file_num];
379 376
             let path = self.join(&entry.name);
380 377
             let download_path = format!("{}.download", get_string(&path));
381
-            std::fs::remove_file(&download_path).ok();
378
+            std::fs::remove_file(download_path).ok();
382 379
         }
383 380
     }
384 381
 
@@ -433,7 +430,7 @@ impl TransferJob {
433 430
         }
434 431
         let name = &self.files[file_num].name;
435 432
         if self.file.is_none() {
436
-            match File::open(self.join(&name)).await {
433
+            match File::open(self.join(name)).await {
437 434
                 Ok(file) => {
438 435
                     self.file = Some(file);
439 436
                     self.file_confirmed = false;
@@ -447,20 +444,15 @@ impl TransferJob {
447 444
                 }
448 445
             }
449 446
         }
450
-        if self.enable_overwrite_detection {
451
-            if !self.file_confirmed() {
452
-                if !self.file_is_waiting() {
453
-                    self.send_current_digest(stream).await?;
454
-                    self.set_file_is_waiting(true);
455
-                }
456
-                return Ok(None);
447
+        if self.enable_overwrite_detection && !self.file_confirmed() {
448
+            if !self.file_is_waiting() {
449
+                self.send_current_digest(stream).await?;
450
+                self.set_file_is_waiting(true);
457 451
             }
452
+            return Ok(None);
458 453
         }
459 454
         const BUF_SIZE: usize = 128 * 1024;
460
-        let mut buf: Vec<u8> = Vec::with_capacity(BUF_SIZE);
461
-        unsafe {
462
-            buf.set_len(BUF_SIZE);
463
-        }
455
+        let mut buf: Vec<u8> = vec![0; BUF_SIZE];
464 456
         let mut compressed = false;
465 457
         let mut offset: usize = 0;
466 458
         loop {
@@ -582,10 +574,7 @@ impl TransferJob {
582 574
     #[inline]
583 575
     pub fn job_completed(&self) -> bool {
584 576
         // has no error, Condition 2
585
-        if !self.enable_overwrite_detection || (!self.file_confirmed && !self.file_is_waiting) {
586
-            return true;
587
-        }
588
-        return false;
577
+        !self.enable_overwrite_detection || (!self.file_confirmed && !self.file_is_waiting)
589 578
     }
590 579
 
591 580
     /// Get job error message, useful for getting status when job had finished
@@ -660,7 +649,7 @@ pub fn new_dir(id: i32, path: String, files: Vec<FileEntry>) -> Message {
660 649
     resp.set_dir(FileDirectory {
661 650
         id,
662 651
         path,
663
-        entries: files.into(),
652
+        entries: files,
664 653
         ..Default::default()
665 654
     });
666 655
     let mut msg_out = Message::new();
@@ -692,7 +681,7 @@ pub fn new_receive(id: i32, path: String, file_num: i32, files: Vec<FileEntry>)
692 681
     action.set_receive(FileTransferReceiveRequest {
693 682
         id,
694 683
         path,
695
-        files: files.into(),
684
+        files,
696 685
         file_num,
697 686
         ..Default::default()
698 687
     });
@@ -736,8 +725,8 @@ pub fn remove_job(id: i32, jobs: &mut Vec<TransferJob>) {
736 725
 }
737 726
 
738 727
 #[inline]
739
-pub fn get_job(id: i32, jobs: &mut Vec<TransferJob>) -> Option<&mut TransferJob> {
740
-    jobs.iter_mut().filter(|x| x.id() == id).next()
728
+pub fn get_job(id: i32, jobs: &mut [TransferJob]) -> Option<&mut TransferJob> {
729
+    jobs.iter_mut().find(|x| x.id() == id)
741 730
 }
742 731
 
743 732
 pub async fn handle_read_jobs(
@@ -789,7 +778,7 @@ pub fn remove_all_empty_dir(path: &PathBuf) -> ResultType<()> {
789 778
                 remove_all_empty_dir(&path.join(&entry.name)).ok();
790 779
             }
791 780
             Ok(FileType::DirLink) | Ok(FileType::FileLink) => {
792
-                std::fs::remove_file(&path.join(&entry.name)).ok();
781
+                std::fs::remove_file(path.join(&entry.name)).ok();
793 782
             }
794 783
             _ => {}
795 784
         }
@@ -813,7 +802,7 @@ pub fn create_dir(dir: &str) -> ResultType<()> {
813 802
 #[inline]
814 803
 pub fn transform_windows_path(entries: &mut Vec<FileEntry>) {
815 804
     for entry in entries {
816
-        entry.name = entry.name.replace("\\", "/");
805
+        entry.name = entry.name.replace('\\', "/");
817 806
     }
818 807
 }
819 808
 

+ 83 - 61
libs/hbb_common/src/lib.rs

@@ -96,8 +96,24 @@ pub type ResultType<F, E = anyhow::Error> = anyhow::Result<F, E>;
96 96
 
97 97
 pub struct AddrMangle();
98 98
 
99
+#[inline]
100
+pub fn try_into_v4(addr: SocketAddr) -> SocketAddr {
101
+    match addr {
102
+        SocketAddr::V6(v6) if !addr.ip().is_loopback() => {
103
+            if let Some(v4) = v6.ip().to_ipv4() {
104
+                SocketAddr::new(IpAddr::V4(v4), addr.port())
105
+            } else {
106
+                addr
107
+            }
108
+        }
109
+        _ => addr,
110
+    }
111
+}
112
+
99 113
 impl AddrMangle {
100 114
     pub fn encode(addr: SocketAddr) -> Vec<u8> {
115
+        // not work with [:1]:<port>
116
+        let addr = try_into_v4(addr);
101 117
         match addr {
102 118
             SocketAddr::V4(addr_v4) => {
103 119
                 let tm = (SystemTime::now()
@@ -129,22 +145,20 @@ impl AddrMangle {
129 145
     }
130 146
 
131 147
     pub fn decode(bytes: &[u8]) -> SocketAddr {
148
+        use std::convert::TryInto;
149
+
132 150
         if bytes.len() > 16 {
133 151
             if bytes.len() != 18 {
134 152
                 return Config::get_any_listen_addr(false);
135 153
             }
136
-            #[allow(invalid_value)]
137
-            let mut tmp: [u8; 2] = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
138
-            tmp.copy_from_slice(&bytes[16..]);
154
+            let tmp: [u8; 2] = bytes[16..].try_into().unwrap();
139 155
             let port = u16::from_le_bytes(tmp);
140
-            #[allow(invalid_value)]
141
-            let mut tmp: [u8; 16] = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
142
-            tmp.copy_from_slice(&bytes[..16]);
156
+            let tmp: [u8; 16] = bytes[..16].try_into().unwrap();
143 157
             let ip = std::net::Ipv6Addr::from(tmp);
144 158
             return SocketAddr::new(IpAddr::V6(ip), port);
145 159
         }
146 160
         let mut padded = [0u8; 16];
147
-        padded[..bytes.len()].copy_from_slice(&bytes);
161
+        padded[..bytes.len()].copy_from_slice(bytes);
148 162
         let number = u128::from_le_bytes(padded);
149 163
         let tm = (number >> 17) & (u32::max_value() as u128);
150 164
         let ip = (((number >> 49) - tm) as u32).to_le_bytes();
@@ -158,21 +172,9 @@ impl AddrMangle {
158 172
 
159 173
 pub fn get_version_from_url(url: &str) -> String {
160 174
     let n = url.chars().count();
161
-    let a = url
162
-        .chars()
163
-        .rev()
164
-        .enumerate()
165
-        .filter(|(_, x)| x == &'-')
166
-        .next()
167
-        .map(|(i, _)| i);
175
+    let a = url.chars().rev().position(|x| x == '-');
168 176
     if let Some(a) = a {
169
-        let b = url
170
-            .chars()
171
-            .rev()
172
-            .enumerate()
173
-            .filter(|(_, x)| x == &'.')
174
-            .next()
175
-            .map(|(i, _)| i);
177
+        let b = url.chars().rev().position(|x| x == '.');
176 178
         if let Some(b) = b {
177 179
             if a > b {
178 180
                 if url
@@ -195,22 +197,30 @@ pub fn get_version_from_url(url: &str) -> String {
195 197
 }
196 198
 
197 199
 pub fn gen_version() {
200
+    if Ok("release".to_owned()) != std::env::var("PROFILE") {
201
+        return;
202
+    }
203
+    println!("cargo:rerun-if-changed=Cargo.toml");
198 204
     use std::io::prelude::*;
199 205
     let mut file = File::create("./src/version.rs").unwrap();
200
-    for line in read_lines("Cargo.toml").unwrap() {
201
-        if let Ok(line) = line {
202
-            let ab: Vec<&str> = line.split("=").map(|x| x.trim()).collect();
203
-            if ab.len() == 2 && ab[0] == "version" {
204
-                file.write_all(format!("pub const VERSION: &str = {};\n", ab[1]).as_bytes())
205
-                    .ok();
206
-                break;
207
-            }
206
+    for line in read_lines("Cargo.toml").unwrap().flatten() {
207
+        let ab: Vec<&str> = line.split('=').map(|x| x.trim()).collect();
208
+        if ab.len() == 2 && ab[0] == "version" {
209
+            file.write_all(format!("pub const VERSION: &str = {};\n", ab[1]).as_bytes())
210
+                .ok();
211
+            break;
208 212
         }
209 213
     }
210 214
     // generate build date
211 215
     let build_date = format!("{}", chrono::Local::now().format("%Y-%m-%d %H:%M"));
212
-    file.write_all(format!("pub const BUILD_DATE: &str = \"{}\";", build_date).as_bytes())
213
-        .ok();
216
+    file.write_all(
217
+        format!(
218
+            "#[allow(dead_code)]\npub const BUILD_DATE: &str = \"{}\";",
219
+            build_date
220
+        )
221
+        .as_bytes(),
222
+    )
223
+    .ok();
214 224
     file.sync_all().ok();
215 225
 }
216 226
 
@@ -230,20 +240,20 @@ pub fn is_valid_custom_id(id: &str) -> bool {
230 240
 
231 241
 pub fn get_version_number(v: &str) -> i64 {
232 242
     let mut n = 0;
233
-    for x in v.split(".") {
243
+    for x in v.split('.') {
234 244
         n = n * 1000 + x.parse::<i64>().unwrap_or(0);
235 245
     }
236 246
     n
237 247
 }
238 248
 
239 249
 pub fn get_modified_time(path: &std::path::Path) -> SystemTime {
240
-    std::fs::metadata(&path)
250
+    std::fs::metadata(path)
241 251
         .map(|m| m.modified().unwrap_or(UNIX_EPOCH))
242 252
         .unwrap_or(UNIX_EPOCH)
243 253
 }
244 254
 
245 255
 pub fn get_created_time(path: &std::path::Path) -> SystemTime {
246
-    std::fs::metadata(&path)
256
+    std::fs::metadata(path)
247 257
         .map(|m| m.created().unwrap_or(UNIX_EPOCH))
248 258
         .unwrap_or(UNIX_EPOCH)
249 259
 }
@@ -276,32 +286,6 @@ pub fn get_time() -> i64 {
276 286
         .unwrap_or(0) as _
277 287
 }
278 288
 
279
-#[cfg(test)]
280
-mod tests {
281
-    use super::*;
282
-    #[test]
283
-    fn test_mangle() {
284
-        let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 16, 32), 21116));
285
-        assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
286
-
287
-        let addr = "[2001:db8::1]:8080".parse::<SocketAddr>().unwrap();
288
-        assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
289
-
290
-        let addr = "[2001:db8:ff::1111]:80".parse::<SocketAddr>().unwrap();
291
-        assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
292
-    }
293
-
294
-    #[test]
295
-    fn test_allow_err() {
296
-        allow_err!(Err("test err") as Result<(), &str>);
297
-        allow_err!(
298
-            Err("test err with msg") as Result<(), &str>,
299
-            "prompt {}",
300
-            "failed"
301
-        );
302
-    }
303
-}
304
-
305 289
 #[inline]
306 290
 pub fn is_ipv4_str(id: &str) -> bool {
307 291
     regex::Regex::new(r"^\d+\.\d+\.\d+\.\d+(:\d+)?$")
@@ -334,9 +318,31 @@ pub fn is_domain_port_str(id: &str) -> bool {
334 318
 }
335 319
 
336 320
 #[cfg(test)]
337
-mod test_lib {
321
+mod test {
338 322
     use super::*;
339 323
 
324
+    #[test]
325
+    fn test_mangle() {
326
+        let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 16, 32), 21116));
327
+        assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
328
+
329
+        let addr = "[2001:db8::1]:8080".parse::<SocketAddr>().unwrap();
330
+        assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
331
+
332
+        let addr = "[2001:db8:ff::1111]:80".parse::<SocketAddr>().unwrap();
333
+        assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr)));
334
+    }
335
+
336
+    #[test]
337
+    fn test_allow_err() {
338
+        allow_err!(Err("test err") as Result<(), &str>);
339
+        allow_err!(
340
+            Err("test err with msg") as Result<(), &str>,
341
+            "prompt {}",
342
+            "failed"
343
+        );
344
+    }
345
+
340 346
     #[test]
341 347
     fn test_ipv6() {
342 348
         assert_eq!(is_ipv6_str("1:2:3"), true);
@@ -373,4 +379,20 @@ mod test_lib {
373 379
         assert_eq!(is_domain_port_str("test.com:0"), true);
374 380
         assert_eq!(is_domain_port_str("test.com:98989"), true);
375 381
     }
382
+
383
+    #[test]
384
+    fn test_mangle2() {
385
+        let addr = "[::ffff:127.0.0.1]:8080".parse().unwrap();
386
+        let addr_v4 = "127.0.0.1:8080".parse().unwrap();
387
+        assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr)), addr_v4);
388
+        assert_eq!(
389
+            AddrMangle::decode(&AddrMangle::encode("[::127.0.0.1]:8080".parse().unwrap())),
390
+            addr_v4
391
+        );
392
+        assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr_v4)), addr_v4);
393
+        let addr_v6 = "[ef::fe]:8080".parse().unwrap();
394
+        assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr_v6)), addr_v6);
395
+        let addr_v6 = "[::1]:8080".parse().unwrap();
396
+        assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr_v6)), addr_v6);
397
+    }
376 398
 }

+ 3 - 3
libs/hbb_common/src/password_security.rs

@@ -104,7 +104,7 @@ pub fn decrypt_str_or_original(s: &str, current_version: &str) -> (String, bool,
104 104
     if s.len() > VERSION_LEN {
105 105
         let version = &s[..VERSION_LEN];
106 106
         if version == "00" {
107
-            if let Ok(v) = decrypt(&s[VERSION_LEN..].as_bytes()) {
107
+            if let Ok(v) = decrypt(s[VERSION_LEN..].as_bytes()) {
108 108
                 return (
109 109
                     String::from_utf8_lossy(&v).to_string(),
110 110
                     true,
@@ -149,7 +149,7 @@ pub fn decrypt_vec_or_original(v: &[u8], current_version: &str) -> (Vec<u8>, boo
149 149
 }
150 150
 
151 151
 fn encrypt(v: &[u8]) -> Result<String, ()> {
152
-    if v.len() > 0 {
152
+    if !v.is_empty() {
153 153
         symmetric_crypt(v, true).map(|v| base64::encode(v, base64::Variant::Original))
154 154
     } else {
155 155
         Err(())
@@ -157,7 +157,7 @@ fn encrypt(v: &[u8]) -> Result<String, ()> {
157 157
 }
158 158
 
159 159
 fn decrypt(v: &[u8]) -> Result<Vec<u8>, ()> {
160
-    if v.len() > 0 {
160
+    if !v.is_empty() {
161 161
         base64::decode(v, base64::Variant::Original).and_then(|v| symmetric_crypt(&v, false))
162 162
     } else {
163 163
         Err(())

+ 4 - 4
libs/hbb_common/src/platform/linux.rs

@@ -32,7 +32,7 @@ pub fn get_display_server() -> String {
32 32
         // loginctl has not given the expected output.  try something else.
33 33
         if let Ok(sid) = std::env::var("XDG_SESSION_ID") {
34 34
             // could also execute "cat /proc/self/sessionid"
35
-            session = sid.to_owned();
35
+            session = sid;
36 36
         }
37 37
         if session.is_empty() {
38 38
             session = run_cmds("cat /proc/self/sessionid".to_owned()).unwrap_or_default();
@@ -63,7 +63,7 @@ fn get_display_server_of_session(session: &str) -> String {
63 63
                 if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty))
64 64
                 // And check if Xorg is running on that tty
65 65
                 {
66
-                    if xorg_results.trim_end().to_string() != "" {
66
+                    if xorg_results.trim_end() != "" {
67 67
                         // If it is, manually return "x11", otherwise return tty
68 68
                         return "x11".to_owned();
69 69
                     }
@@ -88,7 +88,7 @@ pub fn get_values_of_seat0(indices: Vec<usize>) -> Vec<String> {
88 88
     if let Ok(output) = run_loginctl(None) {
89 89
         for line in String::from_utf8_lossy(&output.stdout).lines() {
90 90
             if line.contains("seat0") {
91
-                if let Some(sid) = line.split_whitespace().nth(0) {
91
+                if let Some(sid) = line.split_whitespace().next() {
92 92
                     if is_active(sid) {
93 93
                         return indices
94 94
                             .into_iter()
@@ -103,7 +103,7 @@ pub fn get_values_of_seat0(indices: Vec<usize>) -> Vec<String> {
103 103
     // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73
104 104
     if let Ok(output) = run_loginctl(None) {
105 105
         for line in String::from_utf8_lossy(&output.stdout).lines() {
106
-            if let Some(sid) = line.split_whitespace().nth(0) {
106
+            if let Some(sid) = line.split_whitespace().next() {
107 107
                 let d = get_display_server_of_session(sid);
108 108
                 if is_active(sid) && d != "tty" {
109 109
                     return indices

+ 6 - 7
libs/hbb_common/src/socket_client.rs

@@ -71,7 +71,7 @@ pub trait IsResolvedSocketAddr {
71 71
 
72 72
 impl IsResolvedSocketAddr for SocketAddr {
73 73
     fn resolve(&self) -> Option<&SocketAddr> {
74
-        Some(&self)
74
+        Some(self)
75 75
     }
76 76
 }
77 77
 
@@ -120,12 +120,12 @@ pub async fn connect_tcp_local<
120 120
     if let Some(target) = target.resolve() {
121 121
         if let Some(local) = local {
122 122
             if local.is_ipv6() && target.is_ipv4() {
123
-                let target = query_nip_io(&target).await?;
124
-                return Ok(FramedStream::new(target, Some(local), ms_timeout).await?);
123
+                let target = query_nip_io(target).await?;
124
+                return FramedStream::new(target, Some(local), ms_timeout).await;
125 125
             }
126 126
         }
127 127
     }
128
-    Ok(FramedStream::new(target, local, ms_timeout).await?)
128
+    FramedStream::new(target, local, ms_timeout).await
129 129
 }
130 130
 
131 131
 #[inline]
@@ -140,15 +140,14 @@ pub fn is_ipv4(target: &TargetAddr<'_>) -> bool {
140 140
 pub async fn query_nip_io(addr: &SocketAddr) -> ResultType<SocketAddr> {
141 141
     tokio::net::lookup_host(format!("{}.nip.io:{}", addr.ip(), addr.port()))
142 142
         .await?
143
-        .filter(|x| x.is_ipv6())
144
-        .next()
143
+        .find(|x| x.is_ipv6())
145 144
         .context("Failed to get ipv6 from nip.io")
146 145
 }
147 146
 
148 147
 #[inline]
149 148
 pub fn ipv4_to_ipv6(addr: String, ipv4: bool) -> String {
150 149
     if !ipv4 && crate::is_ipv4_str(&addr) {
151
-        if let Some(ip) = addr.split(":").next() {
150
+        if let Some(ip) = addr.split(':').next() {
152 151
             return addr.replace(ip, &format!("{}.nip.io", ip));
153 152
         }
154 153
     }

+ 9 - 7
libs/hbb_common/src/tcp.rs

@@ -1,4 +1,5 @@
1 1
 use crate::{bail, bytes_codec::BytesCodec, ResultType};
2
+use anyhow::Context as AnyhowCtx;
2 3
 use bytes::{BufMut, Bytes, BytesMut};
3 4
 use futures::{SinkExt, StreamExt};
4 5
 use protobuf::Message;
@@ -209,7 +210,7 @@ impl FramedStream {
209 210
             if let Some(Ok(bytes)) = res.as_mut() {
210 211
                 key.2 += 1;
211 212
                 let nonce = Self::get_nonce(key.2);
212
-                match secretbox::open(&bytes, &nonce, &key.0) {
213
+                match secretbox::open(bytes, &nonce, &key.0) {
213 214
                     Ok(res) => {
214 215
                         bytes.clear();
215 216
                         bytes.put_slice(&res);
@@ -245,16 +246,17 @@ impl FramedStream {
245 246
 
246 247
 const DEFAULT_BACKLOG: u32 = 128;
247 248
 
248
-#[allow(clippy::never_loop)]
249 249
 pub async fn new_listener<T: ToSocketAddrs>(addr: T, reuse: bool) -> ResultType<TcpListener> {
250 250
     if !reuse {
251 251
         Ok(TcpListener::bind(addr).await?)
252 252
     } else {
253
-        for addr in lookup_host(&addr).await? {
254
-            let socket = new_socket(addr, true)?;
255
-            return Ok(socket.listen(DEFAULT_BACKLOG)?);
256
-        }
257
-        bail!("could not resolve to any address");
253
+        let addr = lookup_host(&addr)
254
+            .await?
255
+            .next()
256
+            .context("could not resolve to any address")?;
257
+        new_socket(addr, true)?
258
+            .listen(DEFAULT_BACKLOG)
259
+            .map_err(anyhow::Error::msg)
258 260
     }
259 261
 }
260 262
 

+ 32 - 38
libs/hbb_common/src/udp.rs

@@ -1,11 +1,11 @@
1
-use crate::{bail, ResultType};
2
-use anyhow::anyhow;
1
+use crate::ResultType;
2
+use anyhow::{anyhow, Context};
3 3
 use bytes::{Bytes, BytesMut};
4 4
 use futures::{SinkExt, StreamExt};
5 5
 use protobuf::Message;
6 6
 use socket2::{Domain, Socket, Type};
7 7
 use std::net::SocketAddr;
8
-use tokio::net::{ToSocketAddrs, UdpSocket};
8
+use tokio::net::{lookup_host, ToSocketAddrs, UdpSocket};
9 9
 use tokio_socks::{udp::Socks5UdpFramed, IntoTargetAddr, TargetAddr, ToProxyAddrs};
10 10
 use tokio_util::{codec::BytesCodec, udp::UdpFramed};
11 11
 
@@ -37,39 +37,31 @@ fn new_socket(addr: SocketAddr, reuse: bool, buf_size: usize) -> Result<Socket,
37 37
         addr,
38 38
         socket.recv_buffer_size()
39 39
     );
40
+    if addr.is_ipv6() && addr.ip().is_unspecified() && addr.port() > 0 {
41
+        socket.set_only_v6(false).ok();
42
+    }
40 43
     socket.bind(&addr.into())?;
41 44
     Ok(socket)
42 45
 }
43 46
 
44 47
 impl FramedSocket {
45 48
     pub async fn new<T: ToSocketAddrs>(addr: T) -> ResultType<Self> {
46
-        let socket = UdpSocket::bind(addr).await?;
47
-        Ok(Self::Direct(UdpFramed::new(socket, BytesCodec::new())))
48
-    }
49
-
50
-    #[allow(clippy::never_loop)]
51
-    pub async fn new_reuse<T: std::net::ToSocketAddrs>(addr: T) -> ResultType<Self> {
52
-        for addr in addr.to_socket_addrs()? {
53
-            let socket = new_socket(addr, true, 0)?.into_udp_socket();
54
-            return Ok(Self::Direct(UdpFramed::new(
55
-                UdpSocket::from_std(socket)?,
56
-                BytesCodec::new(),
57
-            )));
58
-        }
59
-        bail!("could not resolve to any address");
49
+        Self::new_reuse(addr, false, 0).await
60 50
     }
61 51
 
62
-    pub async fn new_with_buf_size<T: std::net::ToSocketAddrs>(
52
+    pub async fn new_reuse<T: ToSocketAddrs>(
63 53
         addr: T,
54
+        reuse: bool,
64 55
         buf_size: usize,
65 56
     ) -> ResultType<Self> {
66
-        for addr in addr.to_socket_addrs()? {
67
-            return Ok(Self::Direct(UdpFramed::new(
68
-                UdpSocket::from_std(new_socket(addr, false, buf_size)?.into_udp_socket())?,
69
-                BytesCodec::new(),
70
-            )));
71
-        }
72
-        bail!("could not resolve to any address");
57
+        let addr = lookup_host(&addr)
58
+            .await?
59
+            .next()
60
+            .context("could not resolve to any address")?;
61
+        Ok(Self::Direct(UdpFramed::new(
62
+            UdpSocket::from_std(new_socket(addr, reuse, buf_size)?.into_udp_socket())?,
63
+            BytesCodec::new(),
64
+        )))
73 65
     }
74 66
 
75 67
     pub async fn new_proxy<'a, 't, P: ToProxyAddrs, T: ToSocketAddrs>(
@@ -104,11 +96,12 @@ impl FramedSocket {
104 96
     ) -> ResultType<()> {
105 97
         let addr = addr.into_target_addr()?.to_owned();
106 98
         let send_data = Bytes::from(msg.write_to_bytes()?);
107
-        let _ = match self {
108
-            Self::Direct(f) => match addr {
109
-                TargetAddr::Ip(addr) => f.send((send_data, addr)).await?,
110
-                _ => {}
111
-            },
99
+        match self {
100
+            Self::Direct(f) => {
101
+                if let TargetAddr::Ip(addr) = addr {
102
+                    f.send((send_data, addr)).await?
103
+                }
104
+            }
112 105
             Self::ProxySocks(f) => f.send((send_data, addr)).await?,
113 106
         };
114 107
         Ok(())
@@ -123,11 +116,12 @@ impl FramedSocket {
123 116
     ) -> ResultType<()> {
124 117
         let addr = addr.into_target_addr()?.to_owned();
125 118
 
126
-        let _ = match self {
127
-            Self::Direct(f) => match addr {
128
-                TargetAddr::Ip(addr) => f.send((Bytes::from(msg), addr)).await?,
129
-                _ => {}
130
-            },
119
+        match self {
120
+            Self::Direct(f) => {
121
+                if let TargetAddr::Ip(addr) = addr {
122
+                    f.send((Bytes::from(msg), addr)).await?
123
+                }
124
+            }
131 125
             Self::ProxySocks(f) => f.send((Bytes::from(msg), addr)).await?,
132 126
         };
133 127
         Ok(())
@@ -165,12 +159,12 @@ impl FramedSocket {
165 159
         }
166 160
     }
167 161
 
168
-    pub fn is_ipv4(&self) -> bool {
162
+    pub fn local_addr(&self) -> Option<SocketAddr> {
169 163
         if let FramedSocket::Direct(x) = self {
170 164
             if let Ok(v) = x.get_ref().local_addr() {
171
-                return v.is_ipv4();
165
+                return Some(v);
172 166
             }
173 167
         }
174
-        true
168
+        None
175 169
     }
176 170
 }

+ 57 - 6
src/common.rs

@@ -1,5 +1,8 @@
1 1
 use clap::App;
2
-use hbb_common::{anyhow::Context, log, ResultType};
2
+use hbb_common::{
3
+    anyhow::{Context, Result},
4
+    log, ResultType,
5
+};
3 6
 use ini::Ini;
4 7
 use sodiumoxide::crypto::sign;
5 8
 use std::{
@@ -9,15 +12,17 @@ use std::{
9 12
     time::{Instant, SystemTime},
10 13
 };
11 14
 
15
+#[allow(dead_code)]
12 16
 pub(crate) fn get_expired_time() -> Instant {
13 17
     let now = Instant::now();
14 18
     now.checked_sub(std::time::Duration::from_secs(3600))
15 19
         .unwrap_or(now)
16 20
 }
17 21
 
22
+#[allow(dead_code)]
18 23
 pub(crate) fn test_if_valid_server(host: &str, name: &str) -> ResultType<SocketAddr> {
19 24
     use std::net::ToSocketAddrs;
20
-    let res = if host.contains(":") {
25
+    let res = if host.contains(':') {
21 26
         host.to_socket_addrs()?.next().context("")
22 27
     } else {
23 28
         format!("{}:{}", host, 0)
@@ -31,9 +36,10 @@ pub(crate) fn test_if_valid_server(host: &str, name: &str) -> ResultType<SocketA
31 36
     res
32 37
 }
33 38
 
39
+#[allow(dead_code)]
34 40
 pub(crate) fn get_servers(s: &str, tag: &str) -> Vec<String> {
35 41
     let servers: Vec<String> = s
36
-        .split(",")
42
+        .split(',')
37 43
         .filter(|x| !x.is_empty() && test_if_valid_server(x, tag).is_ok())
38 44
         .map(|x| x.to_owned())
39 45
         .collect();
@@ -41,17 +47,19 @@ pub(crate) fn get_servers(s: &str, tag: &str) -> Vec<String> {
41 47
     servers
42 48
 }
43 49
 
50
+#[allow(dead_code)]
44 51
 #[inline]
45 52
 fn arg_name(name: &str) -> String {
46
-    name.to_uppercase().replace("_", "-")
53
+    name.to_uppercase().replace('_', "-")
47 54
 }
48 55
 
56
+#[allow(dead_code)]
49 57
 pub fn init_args(args: &str, name: &str, about: &str) {
50 58
     let matches = App::new(name)
51 59
         .version(crate::version::VERSION)
52 60
         .author("Purslane Ltd. <info@rustdesk.com>")
53 61
         .about(about)
54
-        .args_from_usage(&args)
62
+        .args_from_usage(args)
55 63
         .get_matches();
56 64
     if let Ok(v) = Ini::load_from_file(".env") {
57 65
         if let Some(section) = v.section(None::<String>) {
@@ -76,16 +84,19 @@ pub fn init_args(args: &str, name: &str, about: &str) {
76 84
     }
77 85
 }
78 86
 
87
+#[allow(dead_code)]
79 88
 #[inline]
80 89
 pub fn get_arg(name: &str) -> String {
81 90
     get_arg_or(name, "".to_owned())
82 91
 }
83 92
 
93
+#[allow(dead_code)]
84 94
 #[inline]
85 95
 pub fn get_arg_or(name: &str, default: String) -> String {
86 96
     std::env::var(arg_name(name)).unwrap_or(default)
87 97
 }
88 98
 
99
+#[allow(dead_code)]
89 100
 #[inline]
90 101
 pub fn now() -> u64 {
91 102
     SystemTime::now()
@@ -118,7 +129,7 @@ pub fn gen_sk(wait: u64) -> (String, Option<sign::SecretKey>) {
118 129
         };
119 130
         let (mut pk, mut sk) = gen_func();
120 131
         for _ in 0..300 {
121
-            if !pk.contains("/") && !pk.contains(":") {
132
+            if !pk.contains('/') && !pk.contains(':') {
122 133
                 break;
123 134
             }
124 135
             (pk, sk) = gen_func();
@@ -138,3 +149,43 @@ pub fn gen_sk(wait: u64) -> (String, Option<sign::SecretKey>) {
138 149
     }
139 150
     ("".to_owned(), None)
140 151
 }
152
+
153
+#[cfg(unix)]
154
+pub async fn listen_signal() -> Result<()> {
155
+    use hbb_common::tokio;
156
+    use hbb_common::tokio::signal::unix::{signal, SignalKind};
157
+
158
+    tokio::spawn(async {
159
+        let mut s = signal(SignalKind::hangup())?;
160
+        let hangup = s.recv();
161
+        let mut s = signal(SignalKind::terminate())?;
162
+        let terminate = s.recv();
163
+        let mut s = signal(SignalKind::interrupt())?;
164
+        let interrupt = s.recv();
165
+        let mut s = signal(SignalKind::quit())?;
166
+        let quit = s.recv();
167
+
168
+        tokio::select! {
169
+            _ = hangup => {
170
+                log::info!("signal hangup");
171
+            }
172
+            _ = terminate => {
173
+                log::info!("signal terminate");
174
+            }
175
+            _ = interrupt => {
176
+                log::info!("signal interrupt");
177
+            }
178
+            _ = quit => {
179
+                log::info!("signal quit");
180
+            }
181
+        }
182
+        Ok(())
183
+    })
184
+    .await?
185
+}
186
+
187
+#[cfg(not(unix))]
188
+pub async fn listen_signal() -> Result<()> {
189
+    let () = std::future::pending().await;
190
+    unreachable!();
191
+}

+ 1 - 35
src/database.rs

@@ -1,6 +1,5 @@
1 1
 use async_trait::async_trait;
2 2
 use hbb_common::{log, ResultType};
3
-use serde_json::value::Value;
4 3
 use sqlx::{
5 4
     sqlite::SqliteConnectOptions, ConnectOptions, Connection, Error as SqlxError, SqliteConnection,
6 5
 };
@@ -8,7 +7,6 @@ use std::{ops::DerefMut, str::FromStr};
8 7
 //use sqlx::postgres::PgPoolOptions;
9 8
 //use sqlx::mysql::MySqlPoolOptions;
10 9
 
11
-pub(crate) type MapValue = serde_json::map::Map<String, Value>;
12 10
 type Pool = deadpool::managed::Pool<DbPool>;
13 11
 
14 12
 pub struct DbPool {
@@ -54,7 +52,7 @@ impl Database {
54 52
             std::fs::File::create(url).ok();
55 53
         }
56 54
         let n: usize = std::env::var("MAX_DATABASE_CONNECTIONS")
57
-            .unwrap_or("1".to_owned())
55
+            .unwrap_or_else(|_| "1".to_owned())
58 56
             .parse()
59 57
             .unwrap_or(1);
60 58
         log::debug!("MAX_DATABASE_CONNECTIONS={}", n);
@@ -105,24 +103,6 @@ impl Database {
105 103
         .await?)
106 104
     }
107 105
 
108
-    #[inline]
109
-    pub async fn get_conn(&self) -> ResultType<deadpool::managed::Object<DbPool>> {
110
-        Ok(self.pool.get().await?)
111
-    }
112
-
113
-    pub async fn update_peer(&self, payload: MapValue, guid: &[u8]) -> ResultType<()> {
114
-        let mut conn = self.get_conn().await?;
115
-        let mut tx = conn.begin().await?;
116
-        if let Some(v) = payload.get("note") {
117
-            let v = get_str(v);
118
-            sqlx::query!("update peer set note = ? where guid = ?", v, guid)
119
-                .execute(&mut tx)
120
-                .await?;
121
-        }
122
-        tx.commit().await?;
123
-        Ok(())
124
-    }
125
-
126 106
     pub async fn insert_peer(
127 107
         &self,
128 108
         id: &str,
@@ -199,17 +179,3 @@ mod tests {
199 179
         hbb_common::futures::future::join_all(jobs).await;
200 180
     }
201 181
 }
202
-
203
-pub(crate) fn get_str(v: &Value) -> Option<&str> {
204
-    match v {
205
-        Value::String(v) => {
206
-            let v = v.trim();
207
-            if v.is_empty() {
208
-                None
209
-            } else {
210
-                Some(v)
211
-            }
212
-        }
213
-        _ => None,
214
-    }
215
-}

+ 26 - 26
src/peer.rs

@@ -1,19 +1,22 @@
1 1
 use crate::common::*;
2 2
 use crate::database;
3 3
 use hbb_common::{
4
+    bytes::Bytes,
4 5
     log,
5 6
     rendezvous_proto::*,
6 7
     tokio::sync::{Mutex, RwLock},
7
-    bytes::Bytes,
8 8
     ResultType,
9 9
 };
10 10
 use serde_derive::{Deserialize, Serialize};
11 11
 use std::{collections::HashMap, collections::HashSet, net::SocketAddr, sync::Arc, time::Instant};
12 12
 
13
+type IpBlockMap = HashMap<String, ((u32, Instant), (HashSet<String>, Instant))>;
14
+type UserStatusMap = HashMap<Vec<u8>, Arc<(Option<Vec<u8>>, bool)>>;
15
+type IpChangesMap = HashMap<String, (Instant, HashMap<String, i32>)>;
13 16
 lazy_static::lazy_static! {
14
-    pub(crate) static ref IP_BLOCKER: Mutex<HashMap<String, ((u32, Instant), (HashSet<String>, Instant))>> = Default::default();
15
-    pub(crate) static ref USER_STATUS: RwLock<HashMap<Vec<u8>, Arc<(Option<Vec<u8>>, bool)>>> = Default::default();
16
-    pub(crate) static ref IP_CHANGES: Mutex<HashMap<String, (Instant, HashMap<String, i32>)>> = Default::default();
17
+    pub(crate) static ref IP_BLOCKER: Mutex<IpBlockMap> = Default::default();
18
+    pub(crate) static ref USER_STATUS: RwLock<UserStatusMap> = Default::default();
19
+    pub(crate) static ref IP_CHANGES: Mutex<IpChangesMap> = Default::default();
17 20
 }
18 21
 pub static IP_CHANGE_DUR: u64 = 180;
19 22
 pub static IP_CHANGE_DUR_X2: u64 = IP_CHANGE_DUR * 2;
@@ -32,9 +35,9 @@ pub(crate) struct Peer {
32 35
     pub(crate) guid: Vec<u8>,
33 36
     pub(crate) uuid: Bytes,
34 37
     pub(crate) pk: Bytes,
35
-    pub(crate) user: Option<Vec<u8>>,
38
+    // pub(crate) user: Option<Vec<u8>>,
36 39
     pub(crate) info: PeerInfo,
37
-    pub(crate) disabled: bool,
40
+    // pub(crate) disabled: bool,
38 41
     pub(crate) reg_pk: (u32, Instant), // how often register_pk
39 42
 }
40 43
 
@@ -47,8 +50,8 @@ impl Default for Peer {
47 50
             uuid: Bytes::new(),
48 51
             pk: Bytes::new(),
49 52
             info: Default::default(),
50
-            user: None,
51
-            disabled: false,
53
+            // user: None,
54
+            // disabled: false,
52 55
             reg_pk: (0, get_expired_time()),
53 56
         }
54 57
     }
@@ -65,7 +68,6 @@ pub(crate) struct PeerMap {
65 68
 impl PeerMap {
66 69
     pub(crate) async fn new() -> ResultType<Self> {
67 70
         let db = std::env::var("DB_URL").unwrap_or({
68
-            #[allow(unused_mut)]
69 71
             let mut db = "db_v2.sqlite3".to_owned();
70 72
             #[cfg(all(windows, not(debug_assertions)))]
71 73
             {
@@ -132,24 +134,22 @@ impl PeerMap {
132 134
 
133 135
     #[inline]
134 136
     pub(crate) async fn get(&self, id: &str) -> Option<LockPeer> {
135
-        let p = self.map.read().await.get(id).map(|x| x.clone());
137
+        let p = self.map.read().await.get(id).cloned();
136 138
         if p.is_some() {
137 139
             return p;
138
-        } else {
139
-            if let Ok(Some(v)) = self.db.get_peer(id).await {
140
-                let peer = Peer {
141
-                    guid: v.guid,
142
-                    uuid: v.uuid.into(),
143
-                    pk: v.pk.into(),
144
-                    user: v.user,
145
-                    info: serde_json::from_str::<PeerInfo>(&v.info).unwrap_or_default(),
146
-                    disabled: v.status == Some(0),
147
-                    ..Default::default()
148
-                };
149
-                let peer = Arc::new(RwLock::new(peer));
150
-                self.map.write().await.insert(id.to_owned(), peer.clone());
151
-                return Some(peer);
152
-            }
140
+        } else if let Ok(Some(v)) = self.db.get_peer(id).await {
141
+            let peer = Peer {
142
+                guid: v.guid,
143
+                uuid: v.uuid.into(),
144
+                pk: v.pk.into(),
145
+                // user: v.user,
146
+                info: serde_json::from_str::<PeerInfo>(&v.info).unwrap_or_default(),
147
+                // disabled: v.status == Some(0),
148
+                ..Default::default()
149
+            };
150
+            let peer = Arc::new(RwLock::new(peer));
151
+            self.map.write().await.insert(id.to_owned(), peer.clone());
152
+            return Some(peer);
153 153
         }
154 154
         None
155 155
     }
@@ -170,7 +170,7 @@ impl PeerMap {
170 170
 
171 171
     #[inline]
172 172
     pub(crate) async fn get_in_memory(&self, id: &str) -> Option<LockPeer> {
173
-        self.map.read().await.get(id).map(|x| x.clone())
173
+        self.map.read().await.get(id).cloned()
174 174
     }
175 175
 
176 176
     #[inline]

+ 51 - 45
src/relay_server.rs

@@ -8,7 +8,7 @@ use hbb_common::{
8 8
     protobuf::Message as _,
9 9
     rendezvous_proto::*,
10 10
     sleep,
11
-    tcp::{new_listener, FramedStream},
11
+    tcp::{listen_any, FramedStream},
12 12
     timeout,
13 13
     tokio::{
14 14
         self,
@@ -37,12 +37,12 @@ lazy_static::lazy_static! {
37 37
 }
38 38
 
39 39
 static mut DOWNGRADE_THRESHOLD: f64 = 0.66;
40
-static mut DOWNGRADE_START_CHECK: usize = 1800_000; // in ms
40
+static mut DOWNGRADE_START_CHECK: usize = 1_800_000; // in ms
41 41
 static mut LIMIT_SPEED: usize = 4 * 1024 * 1024; // in bit/s
42 42
 static mut TOTAL_BANDWIDTH: usize = 1024 * 1024 * 1024; // in bit/s
43 43
 static mut SINGLE_BANDWIDTH: usize = 16 * 1024 * 1024; // in bit/s
44
-const BLACKLIST_FILE: &'static str = "blacklist.txt";
45
-const BLOCKLIST_FILE: &'static str = "blocklist.txt";
44
+const BLACKLIST_FILE: &str = "blacklist.txt";
45
+const BLOCKLIST_FILE: &str = "blocklist.txt";
46 46
 
47 47
 #[tokio::main(flavor = "multi_thread")]
48 48
 pub async fn start(port: &str, key: &str) -> ResultType<()> {
@@ -50,8 +50,8 @@ pub async fn start(port: &str, key: &str) -> ResultType<()> {
50 50
     if let Ok(mut file) = std::fs::File::open(BLACKLIST_FILE) {
51 51
         let mut contents = String::new();
52 52
         if file.read_to_string(&mut contents).is_ok() {
53
-            for x in contents.split("\n") {
54
-                if let Some(ip) = x.trim().split(' ').nth(0) {
53
+            for x in contents.split('\n') {
54
+                if let Some(ip) = x.trim().split(' ').next() {
55 55
                     BLACKLIST.write().await.insert(ip.to_owned());
56 56
                 }
57 57
             }
@@ -65,8 +65,8 @@ pub async fn start(port: &str, key: &str) -> ResultType<()> {
65 65
     if let Ok(mut file) = std::fs::File::open(BLOCKLIST_FILE) {
66 66
         let mut contents = String::new();
67 67
         if file.read_to_string(&mut contents).is_ok() {
68
-            for x in contents.split("\n") {
69
-                if let Some(ip) = x.trim().split(' ').nth(0) {
68
+            for x in contents.split('\n') {
69
+                if let Some(ip) = x.trim().split(' ').next() {
70 70
                     BLOCKLIST.write().await.insert(ip.to_owned());
71 71
                 }
72 72
             }
@@ -77,19 +77,21 @@ pub async fn start(port: &str, key: &str) -> ResultType<()> {
77 77
         BLOCKLIST_FILE,
78 78
         BLOCKLIST.read().await.len()
79 79
     );
80
-    let addr = format!("0.0.0.0:{}", port);
81
-    log::info!("Listening on tcp {}", addr);
82
-    let addr2 = format!("0.0.0.0:{}", port.parse::<u16>().unwrap() + 2);
83
-    log::info!("Listening on websocket {}", addr2);
84
-    loop {
85
-        log::info!("Start");
86
-        io_loop(
87
-            new_listener(&addr, false).await?,
88
-            new_listener(&addr2, false).await?,
89
-            &key,
90
-        )
91
-        .await;
92
-    }
80
+    let port: u16 = port.parse()?;
81
+    log::info!("Listening on tcp :{}", port);
82
+    let port2 = port + 2;
83
+    log::info!("Listening on websocket :{}", port2);
84
+    let main_task = async move {
85
+        loop {
86
+            log::info!("Start");
87
+            io_loop(listen_any(port).await?, listen_any(port2).await?, &key).await;
88
+        }
89
+    };
90
+    let listen_signal = crate::common::listen_signal();
91
+    tokio::select!(
92
+        res = main_task => res,
93
+        res = listen_signal => res,
94
+    )
93 95
 }
94 96
 
95 97
 fn check_params() {
@@ -151,8 +153,10 @@ fn check_params() {
151 153
 }
152 154
 
153 155
 async fn check_cmd(cmd: &str, limiter: Limiter) -> String {
156
+    use std::fmt::Write;
157
+
154 158
     let mut res = "".to_owned();
155
-    let mut fds = cmd.trim().split(" ");
159
+    let mut fds = cmd.trim().split(' ');
156 160
     match fds.next() {
157 161
         Some("h") => {
158 162
             res = format!(
@@ -173,7 +177,7 @@ async fn check_cmd(cmd: &str, limiter: Limiter) -> String {
173 177
         }
174 178
         Some("blacklist-add" | "ba") => {
175 179
             if let Some(ip) = fds.next() {
176
-                for ip in ip.split("|") {
180
+                for ip in ip.split('|') {
177 181
                     BLACKLIST.write().await.insert(ip.to_owned());
178 182
                 }
179 183
             }
@@ -183,7 +187,7 @@ async fn check_cmd(cmd: &str, limiter: Limiter) -> String {
183 187
                 if ip == "all" {
184 188
                     BLACKLIST.write().await.clear();
185 189
                 } else {
186
-                    for ip in ip.split("|") {
190
+                    for ip in ip.split('|') {
187 191
                         BLACKLIST.write().await.remove(ip);
188 192
                     }
189 193
                 }
@@ -194,13 +198,13 @@ async fn check_cmd(cmd: &str, limiter: Limiter) -> String {
194 198
                 res = format!("{}\n", BLACKLIST.read().await.get(ip).is_some());
195 199
             } else {
196 200
                 for ip in BLACKLIST.read().await.clone().into_iter() {
197
-                    res += &format!("{}\n", ip);
201
+                    let _ = writeln!(res, "{ip}");
198 202
                 }
199 203
             }
200 204
         }
201 205
         Some("blocklist-add" | "Ba") => {
202 206
             if let Some(ip) = fds.next() {
203
-                for ip in ip.split("|") {
207
+                for ip in ip.split('|') {
204 208
                     BLOCKLIST.write().await.insert(ip.to_owned());
205 209
                 }
206 210
             }
@@ -210,7 +214,7 @@ async fn check_cmd(cmd: &str, limiter: Limiter) -> String {
210 214
                 if ip == "all" {
211 215
                     BLOCKLIST.write().await.clear();
212 216
                 } else {
213
-                    for ip in ip.split("|") {
217
+                    for ip in ip.split('|') {
214 218
                         BLOCKLIST.write().await.remove(ip);
215 219
                     }
216 220
                 }
@@ -221,7 +225,7 @@ async fn check_cmd(cmd: &str, limiter: Limiter) -> String {
221 225
                 res = format!("{}\n", BLOCKLIST.read().await.get(ip).is_some());
222 226
             } else {
223 227
                 for ip in BLOCKLIST.read().await.clone().into_iter() {
224
-                    res += &format!("{}\n", ip);
228
+                    let _ = writeln!(res, "{ip}");
225 229
                 }
226 230
             }
227 231
         }
@@ -306,15 +310,16 @@ async fn check_cmd(cmd: &str, limiter: Limiter) -> String {
306 310
                 .read()
307 311
                 .await
308 312
                 .iter()
309
-                .map(|x| (x.0.clone(), x.1.clone()))
313
+                .map(|x| (x.0.clone(), *x.1))
310 314
                 .collect();
311 315
             tmp.sort_by(|a, b| ((b.1).1).partial_cmp(&(a.1).1).unwrap());
312 316
             for (ip, (elapsed, total, highest, speed)) in tmp {
313
-                if elapsed <= 0 {
317
+                if elapsed == 0 {
314 318
                     continue;
315 319
                 }
316
-                res += &format!(
317
-                    "{}: {}s {:.2}MB {}kb/s {}kb/s {}kb/s\n",
320
+                let _ = writeln!(
321
+                    res,
322
+                    "{}: {}s {:.2}MB {}kb/s {}kb/s {}kb/s",
318 323
                     ip,
319 324
                     elapsed / 1000,
320 325
                     total as f64 / 1024. / 1024. / 8.,
@@ -489,7 +494,7 @@ async fn relay(
489 494
                     total_limiter.consume(nb).await;
490 495
                     total += nb;
491 496
                     total_s += nb;
492
-                    if bytes.len() > 0 {
497
+                    if !bytes.is_empty() {
493 498
                         stream.send_raw(bytes.into()).await?;
494 499
                     }
495 500
                 } else {
@@ -508,7 +513,7 @@ async fn relay(
508 513
                     total_limiter.consume(nb).await;
509 514
                     total += nb;
510 515
                     total_s += nb;
511
-                    if bytes.len() > 0 {
516
+                    if !bytes.is_empty() {
512 517
                         peer.send_raw(bytes.into()).await?;
513 518
                     }
514 519
                 } else {
@@ -530,7 +535,7 @@ async fn relay(
530 535
             }
531 536
             blacked = BLACKLIST.read().await.get(&ip).is_some();
532 537
             tm = std::time::Instant::now();
533
-            let speed = total_s / (n as usize);
538
+            let speed = total_s / n;
534 539
             if speed > highest_s {
535 540
                 highest_s = speed;
536 541
             }
@@ -540,16 +545,17 @@ async fn relay(
540 545
                 (elapsed as _, total as _, highest_s as _, speed as _),
541 546
             );
542 547
             total_s = 0;
543
-            if elapsed > unsafe { DOWNGRADE_START_CHECK } && !downgrade {
544
-                if total > elapsed * downgrade_threshold {
545
-                    downgrade = true;
546
-                    log::info!(
547
-                        "Downgrade {}, exceed downgrade threshold {}bit/ms in {}ms",
548
-                        id,
549
-                        downgrade_threshold,
550
-                        elapsed
551
-                    );
552
-                }
548
+            if elapsed > unsafe { DOWNGRADE_START_CHECK }
549
+                && !downgrade
550
+                && total > elapsed * downgrade_threshold
551
+            {
552
+                downgrade = true;
553
+                log::info!(
554
+                    "Downgrade {}, exceed downgrade threshold {}bit/ms in {}ms",
555
+                    id,
556
+                    downgrade_threshold,
557
+                    elapsed
558
+                );
553 559
             }
554 560
         }
555 561
     }

+ 110 - 83
src/rendezvous_server.rs

@@ -4,6 +4,7 @@ use hbb_common::{
4 4
     allow_err,
5 5
     bytes::{Bytes, BytesMut},
6 6
     bytes_codec::BytesCodec,
7
+    config,
7 8
     futures::future::join_all,
8 9
     futures_util::{
9 10
         sink::SinkExt,
@@ -15,7 +16,7 @@ use hbb_common::{
15 16
         register_pk_response::Result::{TOO_FREQUENT, UUID_MISMATCH},
16 17
         *,
17 18
     },
18
-    tcp::{new_listener, FramedStream},
19
+    tcp::{listen_any, FramedStream},
19 20
     timeout,
20 21
     tokio::{
21 22
         self,
@@ -25,6 +26,7 @@ use hbb_common::{
25 26
         time::{interval, Duration},
26 27
     },
27 28
     tokio_util::codec::Framed,
29
+    try_into_v4,
28 30
     udp::FramedSocket,
29 31
     AddrMangle, ResultType,
30 32
 };
@@ -32,7 +34,7 @@ use ipnetwork::Ipv4Network;
32 34
 use sodiumoxide::crypto::sign;
33 35
 use std::{
34 36
     collections::HashMap,
35
-    net::{IpAddr, Ipv4Addr, SocketAddr},
37
+    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
36 38
     sync::Arc,
37 39
     time::Instant,
38 40
 };
@@ -40,7 +42,7 @@ const ADDR_127: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
40 42
 
41 43
 #[derive(Clone, Debug)]
42 44
 enum Data {
43
-    Msg(RendezvousMessage, SocketAddr),
45
+    Msg(Box<RendezvousMessage>, SocketAddr),
44 46
     RelayServers0(String),
45 47
     RelayServers(RelayServers),
46 48
 }
@@ -92,15 +94,15 @@ impl RendezvousServer {
92 94
     pub async fn start(port: i32, serial: i32, key: &str, rmem: usize) -> ResultType<()> {
93 95
         let (key, sk) = Self::get_server_sk(key);
94 96
         let addr = format!("0.0.0.0:{}", port);
95
-        let addr2 = format!("0.0.0.0:{}", port - 1);
96
-        let addr3 = format!("0.0.0.0:{}", port + 2);
97
+        let nat_port = port - 1;
98
+        let ws_port = port + 2;
97 99
         let pm = PeerMap::new().await?;
98 100
         log::info!("serial={}", serial);
99 101
         let rendezvous_servers = get_servers(&get_arg("rendezvous-servers"), "rendezvous-servers");
100
-        log::info!("Listening on tcp/udp {}", addr);
101
-        log::info!("Listening on tcp {}, extra port for NAT test", addr2);
102
-        log::info!("Listening on websocket {}", addr3);
103
-        let mut socket = FramedSocket::new_with_buf_size(&addr, rmem).await?;
102
+        log::info!("Listening on tcp/udp :{}", port);
103
+        log::info!("Listening on tcp :{}, extra port for NAT test", nat_port);
104
+        log::info!("Listening on websocket :{}", ws_port);
105
+        let mut socket = create_udp_listener(port, rmem).await?;
104 106
         let (tx, mut rx) = mpsc::unbounded_channel::<Data>();
105 107
         let software_url = get_arg("software-url");
106 108
         let version = hbb_common::get_version_from_url(&software_url);
@@ -138,9 +140,9 @@ impl RendezvousServer {
138 140
         log::info!("local-ip: {:?}", rs.inner.local_ip);
139 141
         std::env::set_var("PORT_FOR_API", port.to_string());
140 142
         rs.parse_relay_servers(&get_arg("relay-servers"));
141
-        let mut listener = new_listener(&addr, false).await?;
142
-        let mut listener2 = new_listener(&addr2, false).await?;
143
-        let mut listener3 = new_listener(&addr3, false).await?;
143
+        let mut listener = create_tcp_listener(port).await?;
144
+        let mut listener2 = create_tcp_listener(nat_port).await?;
145
+        let mut listener3 = create_tcp_listener(ws_port).await?;
144 146
         let test_addr = std::env::var("TEST_HBBS").unwrap_or_default();
145 147
         if std::env::var("ALWAYS_USE_RELAY")
146 148
             .unwrap_or_default()
@@ -170,37 +172,44 @@ impl RendezvousServer {
170 172
                 allow_err!(test_hbbs(test_addr).await);
171 173
             });
172 174
         };
173
-        loop {
174
-            log::info!("Start");
175
-            match rs
176
-                .io_loop(
177
-                    &mut rx,
178
-                    &mut listener,
179
-                    &mut listener2,
180
-                    &mut listener3,
181
-                    &mut socket,
182
-                    &key,
183
-                )
184
-                .await
185
-            {
186
-                LoopFailure::UdpSocket => {
187
-                    drop(socket);
188
-                    socket = FramedSocket::new_with_buf_size(&addr, rmem).await?;
189
-                }
190
-                LoopFailure::Listener => {
191
-                    drop(listener);
192
-                    listener = new_listener(&addr, false).await?;
193
-                }
194
-                LoopFailure::Listener2 => {
195
-                    drop(listener2);
196
-                    listener2 = new_listener(&addr2, false).await?;
197
-                }
198
-                LoopFailure::Listener3 => {
199
-                    drop(listener3);
200
-                    listener3 = new_listener(&addr3, false).await?;
175
+        let main_task = async move {
176
+            loop {
177
+                log::info!("Start");
178
+                match rs
179
+                    .io_loop(
180
+                        &mut rx,
181
+                        &mut listener,
182
+                        &mut listener2,
183
+                        &mut listener3,
184
+                        &mut socket,
185
+                        &key,
186
+                    )
187
+                    .await
188
+                {
189
+                    LoopFailure::UdpSocket => {
190
+                        drop(socket);
191
+                        socket = create_udp_listener(port, rmem).await?;
192
+                    }
193
+                    LoopFailure::Listener => {
194
+                        drop(listener);
195
+                        listener = create_tcp_listener(port).await?;
196
+                    }
197
+                    LoopFailure::Listener2 => {
198
+                        drop(listener2);
199
+                        listener2 = create_tcp_listener(nat_port).await?;
200
+                    }
201
+                    LoopFailure::Listener3 => {
202
+                        drop(listener3);
203
+                        listener3 = create_tcp_listener(ws_port).await?;
204
+                    }
201 205
                 }
202 206
             }
203
-        }
207
+        };
208
+        let listen_signal = listen_signal();
209
+        tokio::select!(
210
+            res = main_task => res,
211
+            res = listen_signal => res,
212
+        )
204 213
     }
205 214
 
206 215
     async fn io_loop(
@@ -226,7 +235,7 @@ impl RendezvousServer {
226 235
                 }
227 236
                 Some(data) = rx.recv() => {
228 237
                     match data {
229
-                        Data::Msg(msg, addr) => { allow_err!(socket.send(&msg, addr).await); }
238
+                        Data::Msg(msg, addr) => { allow_err!(socket.send(msg.as_ref(), addr).await); }
230 239
                         Data::RelayServers0(rs) => { self.parse_relay_servers(&rs); }
231 240
                         Data::RelayServers(rs) => { self.relay_servers = Arc::new(rs); }
232 241
                     }
@@ -296,11 +305,11 @@ impl RendezvousServer {
296 305
         socket: &mut FramedSocket,
297 306
         key: &str,
298 307
     ) -> ResultType<()> {
299
-        if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) {
308
+        if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(bytes) {
300 309
             match msg_in.union {
301 310
                 Some(rendezvous_message::Union::RegisterPeer(rp)) => {
302 311
                     // B registered
303
-                    if rp.id.len() > 0 {
312
+                    if !rp.id.is_empty() {
304 313
                         log::trace!("New peer registered: {:?} {:?}", &rp.id, &addr);
305 314
                         self.update_addr(rp.id, addr, socket).await?;
306 315
                         if self.inner.serial > rp.serial {
@@ -377,12 +386,10 @@ impl RendezvousServer {
377 386
                                 *tm = Instant::now();
378 387
                                 ips.clear();
379 388
                                 ips.insert(ip.clone(), 1);
389
+                            } else if let Some(v) = ips.get_mut(&ip) {
390
+                                *v += 1;
380 391
                             } else {
381
-                                if let Some(v) = ips.get_mut(&ip) {
382
-                                    *v += 1;
383
-                                } else {
384
-                                    ips.insert(ip.clone(), 1);
385
-                                }
392
+                                ips.insert(ip.clone(), 1);
386 393
                             }
387 394
                         } else {
388 395
                             lock.insert(
@@ -465,27 +472,27 @@ impl RendezvousServer {
465 472
         key: &str,
466 473
         ws: bool,
467 474
     ) -> bool {
468
-        if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) {
475
+        if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(bytes) {
469 476
             match msg_in.union {
470 477
                 Some(rendezvous_message::Union::PunchHoleRequest(ph)) => {
471 478
                     // there maybe several attempt, so sink can be none
472 479
                     if let Some(sink) = sink.take() {
473
-                        self.tcp_punch.lock().await.insert(addr, sink);
480
+                        self.tcp_punch.lock().await.insert(try_into_v4(addr), sink);
474 481
                     }
475
-                    allow_err!(self.handle_tcp_punch_hole_request(addr, ph, &key, ws).await);
482
+                    allow_err!(self.handle_tcp_punch_hole_request(addr, ph, key, ws).await);
476 483
                     return true;
477 484
                 }
478 485
                 Some(rendezvous_message::Union::RequestRelay(mut rf)) => {
479 486
                     // there maybe several attempt, so sink can be none
480 487
                     if let Some(sink) = sink.take() {
481
-                        self.tcp_punch.lock().await.insert(addr, sink);
488
+                        self.tcp_punch.lock().await.insert(try_into_v4(addr), sink);
482 489
                     }
483 490
                     if let Some(peer) = self.pm.get_in_memory(&rf.id).await {
484 491
                         let mut msg_out = RendezvousMessage::new();
485 492
                         rf.socket_addr = AddrMangle::encode(addr).into();
486 493
                         msg_out.set_request_relay(rf);
487 494
                         let peer_addr = peer.read().await.socket_addr;
488
-                        self.tx.send(Data::Msg(msg_out, peer_addr)).ok();
495
+                        self.tx.send(Data::Msg(msg_out.into(), peer_addr)).ok();
489 496
                     }
490 497
                     return true;
491 498
                 }
@@ -740,14 +747,14 @@ impl RendezvousServer {
740 747
                     ..Default::default()
741 748
                 });
742 749
             }
743
-            return Ok((msg_out, Some(peer_addr)));
750
+            Ok((msg_out, Some(peer_addr)))
744 751
         } else {
745 752
             let mut msg_out = RendezvousMessage::new();
746 753
             msg_out.set_punch_hole_response(PunchHoleResponse {
747 754
                 failure: punch_hole_response::Failure::ID_NOT_EXIST.into(),
748 755
                 ..Default::default()
749 756
             });
750
-            return Ok((msg_out, None));
757
+            Ok((msg_out, None))
751 758
         }
752 759
     }
753 760
 
@@ -758,8 +765,8 @@ impl RendezvousServer {
758 765
         peers: Vec<String>,
759 766
     ) -> ResultType<()> {
760 767
         let mut states = BytesMut::zeroed((peers.len() + 7) / 8);
761
-        for i in 0..peers.len() {
762
-            if let Some(peer) = self.pm.get_in_memory(&peers[i]).await {
768
+        for (i, peer_id) in peers.iter().enumerate() {
769
+            if let Some(peer) = self.pm.get_in_memory(peer_id).await {
763 770
                 let elapsed = peer.read().await.last_reg_time.elapsed().as_millis() as i32;
764 771
                 // bytes index from left to right
765 772
                 let states_idx = i / 8;
@@ -825,7 +832,7 @@ impl RendezvousServer {
825 832
     ) -> ResultType<()> {
826 833
         let (msg, to_addr) = self.handle_punch_hole_request(addr, ph, key, ws).await?;
827 834
         if let Some(addr) = to_addr {
828
-            self.tx.send(Data::Msg(msg, addr))?;
835
+            self.tx.send(Data::Msg(msg.into(), addr))?;
829 836
         } else {
830 837
             self.send_to_tcp_sync(msg, addr).await?;
831 838
         }
@@ -841,7 +848,7 @@ impl RendezvousServer {
841 848
     ) -> ResultType<()> {
842 849
         let (msg, to_addr) = self.handle_punch_hole_request(addr, ph, key, false).await?;
843 850
         self.tx.send(Data::Msg(
844
-            msg,
851
+            msg.into(),
845 852
             match to_addr {
846 853
                 Some(addr) => addr,
847 854
                 None => addr,
@@ -900,8 +907,10 @@ impl RendezvousServer {
900 907
     }
901 908
 
902 909
     async fn check_cmd(&self, cmd: &str) -> String {
910
+        use std::fmt::Write as _;
911
+
903 912
         let mut res = "".to_owned();
904
-        let mut fds = cmd.trim().split(" ");
913
+        let mut fds = cmd.trim().split(' ');
905 914
         match fds.next() {
906 915
             Some("h") => {
907 916
                 res = format!(
@@ -919,7 +928,7 @@ impl RendezvousServer {
919 928
                     self.tx.send(Data::RelayServers0(rs.to_owned())).ok();
920 929
                 } else {
921 930
                     for ip in self.relay_servers.iter() {
922
-                        res += &format!("{}\n", ip);
931
+                        let _ = writeln!(res, "{ip}");
923 932
                     }
924 933
                 }
925 934
             }
@@ -935,8 +944,9 @@ impl RendezvousServer {
935 944
                 if start < 0 {
936 945
                     if let Some(ip) = ip {
937 946
                         if let Some((a, b)) = lock.get(ip) {
938
-                            res += &format!(
939
-                                "{}/{}s {}/{}s\n",
947
+                            let _ = writeln!(
948
+                                res,
949
+                                "{}/{}s {}/{}s",
940 950
                                 a.0,
941 951
                                 a.1.elapsed().as_secs(),
942 952
                                 b.0.len(),
@@ -961,8 +971,9 @@ impl RendezvousServer {
961 971
                             continue;
962 972
                         }
963 973
                         if let Some((ip, (a, b))) = x {
964
-                            res += &format!(
965
-                                "{}: {}/{}s {}/{}s\n",
974
+                            let _ = writeln!(
975
+                                res,
976
+                                "{}: {}/{}s {}/{}s",
966 977
                                 ip,
967 978
                                 a.0,
968 979
                                 a.1.elapsed().as_secs(),
@@ -979,10 +990,10 @@ impl RendezvousServer {
979 990
                 res = format!("{}\n", lock.len());
980 991
                 let id = fds.next();
981 992
                 let mut start = id.map(|x| x.parse::<i32>().unwrap_or(-1)).unwrap_or(-1);
982
-                if start < 0 || start > 10_000_000 {
993
+                if !(0..=10_000_000).contains(&start) {
983 994
                     if let Some(id) = id {
984 995
                         if let Some((tm, ips)) = lock.get(id) {
985
-                            res += &format!("{}s {:?}\n", tm.elapsed().as_secs(), ips);
996
+                            let _ = writeln!(res, "{}s {:?}", tm.elapsed().as_secs(), ips);
986 997
                         }
987 998
                         if fds.next() == Some("-") {
988 999
                             lock.remove(id);
@@ -1002,7 +1013,7 @@ impl RendezvousServer {
1002 1013
                             continue;
1003 1014
                         }
1004 1015
                         if let Some((id, (tm, ips))) = x {
1005
-                            res += &format!("{}: {}s {:?}\n", id, tm.elapsed().as_secs(), ips,);
1016
+                            let _ = writeln!(res, "{}: {}s {:?}", id, tm.elapsed().as_secs(), ips,);
1006 1017
                         }
1007 1018
                     }
1008 1019
                 }
@@ -1016,7 +1027,7 @@ impl RendezvousServer {
1016 1027
                     }
1017 1028
                     self.tx.send(Data::RelayServers0(rs.to_owned())).ok();
1018 1029
                 } else {
1019
-                    res += &format!("ALWAYS_USE_RELAY: {:?}\n", unsafe { ALWAYS_USE_RELAY });
1030
+                    let _ = writeln!(res, "ALWAYS_USE_RELAY: {:?}", unsafe { ALWAYS_USE_RELAY });
1020 1031
                 }
1021 1032
             }
1022 1033
             Some("test-geo" | "tg") => {
@@ -1039,7 +1050,7 @@ impl RendezvousServer {
1039 1050
 
1040 1051
     async fn handle_listener2(&self, stream: TcpStream, addr: SocketAddr) {
1041 1052
         let mut rs = self.clone();
1042
-        if addr.ip().to_string() == "127.0.0.1" {
1053
+        if addr.ip().is_loopback() {
1043 1054
             tokio::spawn(async move {
1044 1055
                 let mut stream = stream;
1045 1056
                 let mut buffer = [0; 64];
@@ -1099,13 +1110,10 @@ impl RendezvousServer {
1099 1110
             let (a, mut b) = ws_stream.split();
1100 1111
             sink = Some(Sink::Ws(a));
1101 1112
             while let Ok(Some(Ok(msg))) = timeout(30_000, b.next()).await {
1102
-                match msg {
1103
-                    tungstenite::Message::Binary(bytes) => {
1104
-                        if !self.handle_tcp(&bytes, &mut sink, addr, key, ws).await {
1105
-                            break;
1106
-                        }
1113
+                if let tungstenite::Message::Binary(bytes) = msg {
1114
+                    if !self.handle_tcp(&bytes, &mut sink, addr, key, ws).await {
1115
+                        break;
1107 1116
                     }
1108
-                    _ => {}
1109 1117
                 }
1110 1118
             }
1111 1119
         } else {
@@ -1131,7 +1139,7 @@ impl RendezvousServer {
1131 1139
         } else {
1132 1140
             match self.pm.get(&id).await {
1133 1141
                 Some(peer) => {
1134
-                    let pk = peer.read().await.pk.clone().into();
1142
+                    let pk = peer.read().await.pk.clone();
1135 1143
                     sign::sign(
1136 1144
                         &hbb_common::message_proto::IdPk {
1137 1145
                             id,
@@ -1140,7 +1148,7 @@ impl RendezvousServer {
1140 1148
                         }
1141 1149
                         .write_to_bytes()
1142 1150
                         .unwrap_or_default(),
1143
-                        &self.inner.sk.as_ref().unwrap(),
1151
+                        self.inner.sk.as_ref().unwrap(),
1144 1152
                     )
1145 1153
                     .into()
1146 1154
                 }
@@ -1196,8 +1204,8 @@ async fn check_relay_servers(rs0: Arc<RelayServers>, tx: Sender) {
1196 1204
     let rs = Arc::new(Mutex::new(Vec::new()));
1197 1205
     for x in rs0.iter() {
1198 1206
         let mut host = x.to_owned();
1199
-        if !host.contains(":") {
1200
-            host = format!("{}:{}", host, hbb_common::config::RELAY_PORT);
1207
+        if !host.contains(':') {
1208
+            host = format!("{}:{}", host, config::RELAY_PORT);
1201 1209
         }
1202 1210
         let rs = rs.clone();
1203 1211
         let x = x.clone();
@@ -1212,7 +1220,7 @@ async fn check_relay_servers(rs0: Arc<RelayServers>, tx: Sender) {
1212 1220
     }
1213 1221
     join_all(futs).await;
1214 1222
     log::debug!("check_relay_servers");
1215
-    let rs = std::mem::replace(&mut *rs.lock().await, Default::default());
1223
+    let rs = std::mem::take(&mut *rs.lock().await);
1216 1224
     if !rs.is_empty() {
1217 1225
         tx.send(Data::RelayServers(rs)).ok();
1218 1226
     }
@@ -1220,7 +1228,7 @@ async fn check_relay_servers(rs0: Arc<RelayServers>, tx: Sender) {
1220 1228
 
1221 1229
 // temp solution to solve udp socket failure
1222 1230
 async fn test_hbbs(addr: SocketAddr) -> ResultType<()> {
1223
-    let mut socket = FramedSocket::new("0.0.0.0:0").await?;
1231
+    let mut socket = FramedSocket::new(config::Config::get_any_listen_addr(addr.is_ipv4())).await?;
1224 1232
     let mut msg_out = RendezvousMessage::new();
1225 1233
     msg_out.set_register_peer(RegisterPeer {
1226 1234
         id: "(:test_hbbs:)".to_owned(),
@@ -1261,3 +1269,22 @@ async fn send_rk_res(
1261 1269
     });
1262 1270
     socket.send(&msg_out, addr).await
1263 1271
 }
1272
+
1273
+async fn create_udp_listener(port: i32, rmem: usize) -> ResultType<FramedSocket> {
1274
+    let addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port as _);
1275
+    if let Ok(s) = FramedSocket::new_reuse(&addr, false, rmem).await {
1276
+        log::debug!("listen on udp {:?}", s.local_addr());
1277
+        return Ok(s);
1278
+    }
1279
+    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port as _);
1280
+    let s = FramedSocket::new_reuse(&addr, false, rmem).await?;
1281
+    log::debug!("listen on udp {:?}", s.local_addr());
1282
+    Ok(s)
1283
+}
1284
+
1285
+#[inline]
1286
+async fn create_tcp_listener(port: i32) -> ResultType<TcpListener> {
1287
+    let s = listen_any(port as _).await?;
1288
+    log::debug!("listen on tcp {:?}", s.local_addr());
1289
+    Ok(s)
1290
+}

+ 12 - 14
src/utils.rs

@@ -33,7 +33,7 @@ fn gen_keypair() {
33 33
 }
34 34
 
35 35
 fn validate_keypair(pk: &str, sk: &str) -> ResultType<()> {
36
-    let sk1 = base64::decode(&sk);
36
+    let sk1 = base64::decode(sk);
37 37
     if sk1.is_err() {
38 38
         bail!("Invalid secret key");
39 39
     }
@@ -45,7 +45,7 @@ fn validate_keypair(pk: &str, sk: &str) -> ResultType<()> {
45 45
     }
46 46
     let secret_key = secret_key.unwrap();
47 47
 
48
-    let pk1 = base64::decode(&pk);
48
+    let pk1 = base64::decode(pk);
49 49
     if pk1.is_err() {
50 50
         bail!("Invalid public key");
51 51
     }
@@ -96,14 +96,13 @@ fn doctor_ip(server_ip_address: std::net::IpAddr, server_address: Option<&str>)
96 96
     // reverse dns lookup
97 97
     // TODO: (check) doesn't seem to do reverse lookup on OSX...
98 98
     let reverse = lookup_addr(&server_ip_address).unwrap();
99
-    if server_address.is_some() {
100
-        if reverse == server_address.unwrap() {
99
+    if let Some(server_address) = server_address {
100
+        if reverse == server_address {
101 101
             println!("Reverse DNS lookup: '{}' MATCHES server address", reverse);
102 102
         } else {
103 103
             println!(
104 104
                 "Reverse DNS lookup: '{}' DOESN'T MATCH server address '{}'",
105
-                reverse,
106
-                server_address.unwrap()
105
+                reverse, server_address
107 106
             );
108 107
         }
109 108
     }
@@ -126,19 +125,18 @@ fn doctor(server_address_unclean: &str) {
126 125
     let server_address2 = server_address3.to_lowercase();
127 126
     let server_address = server_address2.as_str();
128 127
     println!("Checking server:  {}\n", server_address);
129
-    let server_ipaddr = server_address.parse::<IpAddr>();
130
-    if server_ipaddr.is_err() {
128
+    if let Ok(server_ipaddr) = server_address.parse::<IpAddr>() {
129
+        // user requested an ip address
130
+        doctor_ip(server_ipaddr, None);
131
+    } else {
131 132
         // the passed string is not an ip address
132 133
         let ips: Vec<std::net::IpAddr> = lookup_host(server_address).unwrap();
133
-        println!("Found {} IP addresses: ", ips.iter().count());
134
+        println!("Found {} IP addresses: ", ips.len());
134 135
 
135 136
         ips.iter().for_each(|ip| println!(" - {ip}"));
136 137
 
137
-        ips.iter().for_each(|ip| doctor_ip(*ip, Some(server_address)));
138
-
139
-    } else {
140
-        // user requested an ip address
141
-        doctor_ip(server_ipaddr.unwrap(), None);
138
+        ips.iter()
139
+            .for_each(|ip| doctor_ip(*ip, Some(server_address)));
142 140
     }
143 141
 }
144 142
 

+ 3 - 2
src/version.rs

@@ -1,2 +1,3 @@
1
-pub const VERSION: &str = "1.1.6";
2
-pub const BUILD_DATE: &str = "2023-01-06 10:39";
1
+pub const VERSION: &str = "1.1.7";
2
+#[allow(dead_code)]
3
+pub const BUILD_DATE: &str = "2023-01-10 22:43";

+ 2 - 0
systemd/rustdesk-hbbr.service

@@ -10,6 +10,8 @@ WorkingDirectory=/var/lib/rustdesk-server/
10 10
 User=
11 11
 Group=
12 12
 Restart=always
13
+StandardOutput=append:/var/log/rustdesk/rustdesk-hbbr.log
14
+StandardError=append:/var/log/rustdesk/rustdesk-hbbr.error
13 15
 # Restart service after 10 seconds if node service crashes
14 16
 RestartSec=10
15 17
 

+ 2 - 0
systemd/rustdesk-hbbs.service

@@ -10,6 +10,8 @@ WorkingDirectory=/var/lib/rustdesk-server/
10 10
 User=
11 11
 Group=
12 12
 Restart=always
13
+StandardOutput=append:/var/log/rustdesk/rustdesk-hbbs.log
14
+StandardError=append:/var/log/rustdesk/rustdesk-hbbs.error
13 15
 # Restart service after 10 seconds if node service crashes
14 16
 RestartSec=10
15 17