Skip to content

Commit af3868c

Browse files
committed
listens on ipv4 and ipv6
1 parent f4c5a46 commit af3868c

7 files changed

Lines changed: 326 additions & 137 deletions

File tree

README.cn.md

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,48 @@
1-
# tsshd
1+
# tsshd - 支持连接迁移的 ssh 服务端
22

33
[![MIT License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://choosealicense.com/licenses/mit/)
44
[![GitHub Release](https://img.shields.io/github/v/release/trzsz/tsshd)](https://github.com/trzsz/tsshd/releases)
55

6-
`tsshd` 类似于 `mosh-server`,而 [`tssh --udp`](https://github.com/trzsz/trzsz-ssh) 类似于 [`mosh`](https://github.com/mobile-shell/mosh)
6+
trzsz-ssh ( tssh ) 与 tsshd 一起,适用于高延迟的弱网连接,切换网络、休眠与唤醒都不会掉线,让 ssh 会话永远保持
77

8-
## 优点简介
8+
tssh 设计为 ssh 客户端的直接替代品,提供与 openssh 完全兼容的基础功能,同时实现其他有用的扩展功能,外加:
99

10-
- 降低延迟( 基于 [QUIC](https://github.com/quic-go/quic-go) / [KCP](https://github.com/xtaci/kcp-go)
10+
- 客户端进入休眠并且迟些再唤醒,或者暂时断开网络,ssh 会话可以保持不掉线。
1111

12-
- 端口转发( 与 openssh 相同,包括 ssh agent 转发和 X11 转发 )
12+
- 客户端换地方接入,更换 IP 地址,任意切换网络等,ssh 会话可以保持不中断。
1313

14-
- 连接迁移( 支持 tssh 客户端休眠与唤醒、网络切换、断线重连等 )
14+
## 功能对比
1515

16-
- 代理跳转 ( 第一跳支持 UDP:`客户端 ---udp--> 跳板机 ---tcp--> 服务器` )
16+
tsshd 的灵感来源于 [mosh](https://github.com/mobile-shell/mosh)`tsshd` 类似于 `mosh-server`,而 `tssh --udp` 类似于 `mosh`
17+
18+
| Feature | mosh ( mosh-server ) | tssh ( tsshd ) |
19+
| ------------------------ | :-----------------------------------------------------------: | :---------------------------------------: |
20+
| 超低延迟 | ?? |[KCP](https://github.com/xtaci/kcp-go) |
21+
| 保持连接 |||
22+
| 切换网络 |||
23+
| 本地回显 & 行编辑 || 无支持计划 |
24+
| 支持多平台 / Windows | [mosh#293](https://github.com/mobile-shell/mosh/issues/293) ||
25+
| SSH X11 转发 | [mosh#41](https://github.com/mobile-shell/mosh/issues/41) ||
26+
| SSH Agent 转发 | [mosh#120](https://github.com/mobile-shell/mosh/issues/120) ||
27+
| SSH 端口转发 | [mosh#337](https://github.com/mobile-shell/mosh/issues/337) ||
28+
| 输出上下滚动 | [mosh#122](https://github.com/mobile-shell/mosh/issues/122) ||
29+
| OSC52 复制粘贴 | [mosh#637](https://github.com/mobile-shell/mosh/issues/637) ||
30+
| tmux -CC 集成 | [mosh#1078](https://github.com/mobile-shell/mosh/issues/1078) ||
31+
| ProxyJump / ProxyCommand | [mosh#970](https://github.com/mobile-shell/mosh/issues/970) | ✅ 第一跳 |
32+
33+
tssh 和 tsshd 的工作方式与 ssh 完全相同,没有计划支持本地回显和行编辑,也不会出现 mosh 的问题:[mosh#1041](https://github.com/mobile-shell/mosh/issues/1041)[mosh#1281](https://github.com/mobile-shell/mosh/issues/1281)[mosh#1295](https://github.com/mobile-shell/mosh/issues/1295) 等。
1734

1835
## 如何使用
1936

2037
1. 在客户端(本地电脑)上安装 [tssh](https://github.com/trzsz/trzsz-ssh)
2138

2239
2. 在服务端(远程机器)上安装 [tsshd](https://github.com/trzsz/tsshd)
2340

24-
3. 使用 `tssh --udp` 登录服务器。如下配置可省略 `--udp` 参数:
41+
3. 使用 `tssh --udp` 登录服务器。`~/.ssh/config` 中如下配置可省略 `--udp` 参数:
2542

2643
```
2744
Host xxx
2845
#!! UdpMode yes
29-
#!! TsshdPath ~/go/bin/tsshd
30-
#!! UdpPort 61000-62000
31-
#!! UdpAliveTimeout 86400
3246
```
3347

3448
## 原理简介
@@ -37,16 +51,37 @@
3751

3852
- `tssh` 会先作为一个 ssh 客户端正常登录到服务器上,然后在服务器上启动一个新的 `tsshd` 进程。
3953

40-
- `tsshd` 进程会随机侦听一个 6100062000 之间的 UDP 端口(可通过 `UdpPort` 配置自定义),并将其端口和密钥通过 ssh 通道发回给 `tssh` 进程。登录的 ssh 连接会被关闭,然后 `tssh` 进程通过 UDP 与 `tsshd` 进程通讯。
54+
- `tsshd` 进程会随机侦听一个 6100161999 之间的 UDP 端口(可通过 `UdpPort` 配置自定义),并将其端口和密钥通过 ssh 通道发回给 `tssh` 进程。登录的 ssh 连接会被关闭,然后 `tssh` 进程通过 UDP 与 `tsshd` 进程通讯。
4155

42-
- `tsshd` 进程会在网络断开超过 24 小时后退出(默认情况下),可以通过修改 `UdpAliveTimeout` 配置来调整(单位:秒)。
56+
## 配置说明
4357

44-
- `tsshd` 支持 `QUIC` 协议和 `KCP` 协议(默认是 `QUIC` 协议),可以命令行指定(如 `-oUdpMode=KCP`),或如下配置:
58+
```
59+
Host xxx
60+
#!! UdpMode KCP
61+
#!! UdpPort 61001-61999
62+
#!! TsshdPath ~/go/bin/tsshd
63+
#!! UdpAliveTimeout 86400
64+
#!! UdpHeartbeatTimeout 3
65+
#!! UdpReconnectTimeout 15
66+
#!! ShowNotificationOnTop yes
67+
#!! ShowFullNotifications yes
68+
```
4569

46-
```
47-
Host xxx
48-
#!! UdpMode KCP
49-
```
70+
- `UdpMode`: `No` (默认为`No`: tssh 工作在 TCP 模式), `Yes` (默认协议: `KCP`), `QUIC` ([QUIC](https://github.com/quic-go/quic-go) 协议), `KCP` ([KCP](https://github.com/xtaci/kcp-go) 协议).
71+
72+
- `UdpPort`: 指定 tsshd 监听的 UDP 端口范围,默认值为 [61001, 61999]
73+
74+
- `TsshdPath`: 指定服务器上 tsshd 二进制程序的路径,如果未配置,则在 $PATH 中查找。
75+
76+
- `UdpAliveTimeout`: 如果断开连接的时间超过 `UdpAliveTimeout` 秒,tssh 和 tsshd 都会退出,不再支持重连。默认值为 86400 秒。
77+
78+
- `UdpHeartbeatTimeout`: 如果断开连接的时间超过 `UdpHeartbeatTimeout` 秒,tssh 将会尝试换条路重新连到服务器。默认值为 3 秒。
79+
80+
- `UdpReconnectTimeout`: 如果断开连接的时间超过 `UdpReconnectTimeout` 秒,tssh 将会显示失去连接的通知公告。默认值为 15 秒。
81+
82+
- `ShowNotificationOnTop`: 是否在屏幕顶部显示失去连接的通知。默认为 yes,这可能会覆盖之前的一些输出。设置为 `No` 在光标当前行显示通知。
83+
84+
- `ShowFullNotifications`: 是显示完整的通知,还是显示简短的通知。默认为 yes,这可能会输出几行通知到屏幕上。设置为 `No` 只输出一行通知。
5085

5186
## 安装方法
5287

@@ -106,7 +141,7 @@
106141

107142
</details>
108143

109-
- ArchLinux 可用 [yay](https://github.com/Jguer/yay) 安装
144+
- ArchLinux 可用 yay 安装
110145

111146
<details><summary><code>yay -S tsshd</code></summary>
112147

README.md

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,90 @@
1-
# tsshd
1+
## tsshd - tssh server that supports connection migration for roaming
22

33
[![MIT License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://choosealicense.com/licenses/mit/)
44
[![GitHub Release](https://img.shields.io/github/v/release/trzsz/tsshd)](https://github.com/trzsz/tsshd/releases)
55
[![中文文档](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-%E6%96%87%E6%A1%A3-blue?style=flat)](https://github.com/trzsz/tsshd/blob/main/README.cn.md)
66

7-
The `tsshd` works like `mosh-server`, while the [`tssh --udp`](https://github.com/trzsz/trzsz-ssh) works like [`mosh`](https://github.com/mobile-shell/mosh).
7+
trzsz-ssh ( tssh ) with tsshd supports intermittent connectivity, allows roaming, and can be used on high-latency links such as cellular data connections, unstable Wi-Fi, etc.
88

9-
## Advantages
9+
It aims to provide complete compatibility with openssh, mirroring all its features, while also offering additional useful features not found in the openssh client, plus:
1010

11-
- Low Latency ( based on [QUIC](https://github.com/quic-go/quic-go) / [KCP](https://github.com/xtaci/kcp-go) )
11+
- Keeps the session alive if the client goes to sleep and wakes up later, or temporarily loses its connection.
1212

13-
- Port Forwarding ( same as openssh, includes ssh agent forwarding and X11 forwarding )
13+
- Allows the client to "roam" and change IP addresses, switching between any networks, while keeping alive.
1414

15-
- Connection Migration ( supports client sleep and wake-up, network switching, reconnection, etc. )
15+
### Comparison
1616

17-
- Proxy Jump ( First hop supports UDP: `Client ---udp--> JumpServer ---tcp--> TargetServer` )
17+
tsshd was inspired by [mosh](https://github.com/mobile-shell/mosh), and the `tsshd` works like `mosh-server`, while the `tssh --udp` works like `mosh`.
1818

19-
## How to use
19+
| Feature | mosh ( mosh-server ) | tssh ( tsshd ) |
20+
| ------------------------- | :-----------------------------------------------------------: | :---------------------------------------: |
21+
| Low Latency | ?? |[KCP](https://github.com/xtaci/kcp-go) |
22+
| Keep Alive |||
23+
| Client Roaming |||
24+
| Local Echo & Line Editing || Not Planned |
25+
| Multi Platform / Windows | [mosh#293](https://github.com/mobile-shell/mosh/issues/293) ||
26+
| SSH X11 Forwarding | [mosh#41](https://github.com/mobile-shell/mosh/issues/41) ||
27+
| SSH Agent Forwarding | [mosh#120](https://github.com/mobile-shell/mosh/issues/120) ||
28+
| SSH Port Forwarding | [mosh#337](https://github.com/mobile-shell/mosh/issues/337) ||
29+
| Output Scrollback | [mosh#122](https://github.com/mobile-shell/mosh/issues/122) ||
30+
| OSC52 Sequence | [mosh#637](https://github.com/mobile-shell/mosh/issues/637) ||
31+
| tmux -CC Integration | [mosh#1078](https://github.com/mobile-shell/mosh/issues/1078) ||
32+
| ProxyJump / ProxyCommand | [mosh#970](https://github.com/mobile-shell/mosh/issues/970) | ✅ First Hop |
33+
34+
tssh and tsshd works exactly like ssh, there are no plans to support local echo and line editing, and will not have the mosh issues: [mosh#1041](https://github.com/mobile-shell/mosh/issues/1041), [mosh#1281](https://github.com/mobile-shell/mosh/issues/1281), [mosh#1295](https://github.com/mobile-shell/mosh/issues/1295), etc.
35+
36+
### How to use
2037

2138
1. Install [tssh](https://github.com/trzsz/trzsz-ssh) on the client ( the user's machine ).
2239

2340
2. Install [tsshd](https://github.com/trzsz/tsshd) on the server ( the remote host ).
2441

25-
3. Use `tssh --udp` to login to the server. Configure as follows to omit `--udp`:
42+
3. Use `tssh --udp` to login to the server. Or configure as follows in `~/.ssh/config` to omit `--udp`:
2643

2744
```
2845
Host xxx
2946
#!! UdpMode yes
30-
#!! TsshdPath ~/go/bin/tsshd
31-
#!! UdpPort 61000-62000
32-
#!! UdpAliveTimeout 86400
3347
```
3448

35-
## How it works
49+
### How it works
3650

3751
- The `tssh` plays the role of `ssh` on the client side, and the `tsshd` plays the role of `sshd` on the server side.
3852

3953
- The `tssh` will first login to the server normally as an ssh client, and then run a new `tsshd` process on the server.
4054

41-
- The `tsshd` process listens on a random udp port between 61000 and 62000 (can be customized by `UdpPort`), and sends its port number and a secret key back to the `tssh` process over the ssh channel. The ssh connection is then shut down, and the `tssh` process communicates with the `tsshd` process over udp.
55+
- The `tsshd` process listens on a random udp port between 61001 and 61999 (can be customized by `UdpPort`), and sends its port number and a secret key back to the `tssh` process over the ssh channel. The ssh connection is then shut down, and the `tssh` process communicates with the `tsshd` process over udp.
4256

43-
- The `tsshd` process will exit if the network is disconnected for more than 24 hours by default, and no longer support reconnection. This can be adjusted by modifying the configuration `UdpAliveTimeout` in seconds.
57+
### Configurations
4458

45-
- The `tsshd` supports `QUIC` protocol and `KCP` protocol (the default is `QUIC`), which can be specified on the command line (such as `-oUdpMode=KCP`), or configured as follows:
59+
```
60+
Host xxx
61+
#!! UdpMode KCP
62+
#!! UdpPort 61001-61999
63+
#!! TsshdPath ~/go/bin/tsshd
64+
#!! UdpAliveTimeout 86400
65+
#!! UdpHeartbeatTimeout 3
66+
#!! UdpReconnectTimeout 15
67+
#!! ShowNotificationOnTop yes
68+
#!! ShowFullNotifications yes
69+
```
4670

47-
```
48-
Host xxx
49-
#!! UdpMode KCP
50-
```
71+
- `UdpMode`: `No` (the default: tssh works in TCP mode), `Yes` (default protocol: `KCP`), `QUIC` ([QUIC](https://github.com/quic-go/quic-go) protocol), `KCP` ([KCP](https://github.com/xtaci/kcp-go) protocol).
72+
73+
- `UdpPort`: Specifies the range of UDP ports that tsshd listens on, the default value is [61001, 61999].
74+
75+
- `TsshdPath`: Specifies the path to the tsshd binary on the server, lookup in $PATH if not configured.
76+
77+
- `UdpAliveTimeout`: If the disconnection lasts longer than `UdpAliveTimeout` in seconds, tssh and tsshd will both exit, and no longer support reconnection. The default is 86400 seconds.
78+
79+
- `UdpHeartbeatTimeout`: If the disconnection lasts longer than `UdpHeartbeatTimeout` in seconds, tssh will try to reconnect to the server by a new path. The default is 3 seconds.
80+
81+
- `UdpReconnectTimeout`: If the disconnection lasts longer than `UdpReconnectTimeout` in seconds, tssh will display a notification indicating that the connection has been lost. The default is 15 seconds.
82+
83+
- `ShowNotificationOnTop`: Whether the connection loss notification is displayed on the top. The default is yes, which may overwrite some of the previous output. Set it to `No` to display notifications on the current line of the cursor.
84+
85+
- `ShowFullNotifications`: Whether to display the full notifications or a brief notification. The default is yes, which may output several lines to the screen. Set it to `No` will output only one line.
5186

52-
## Installation
87+
### Installation
5388

5489
- Install with apt on Ubuntu
5590

@@ -107,7 +142,7 @@ The `tsshd` works like `mosh-server`, while the [`tssh --udp`](https://github.co
107142
108143
</details>
109144
110-
- Install with [yay](https://github.com/Jguer/yay) on ArchLinux
145+
- Install with yay on ArchLinux
111146
112147
<details><summary><code>yay -S tsshd</code></summary>
113148
@@ -145,10 +180,10 @@ The `tsshd` works like `mosh-server`, while the [`tssh --udp`](https://github.co
145180
146181
- Download from the [GitHub Releases](https://github.com/trzsz/tsshd/releases), unzip and add to `PATH` environment.
147182
148-
## Contact
183+
### Contact
149184
150185
Feel free to email the author <lonnywong@qq.com>, or create an [issue](https://github.com/trzsz/tsshd/issues). Welcome to join the QQ group: 318578930.
151186
152-
## Sponsor
187+
### Sponsor
153188
154189
[❤️ Sponsor trzsz ❤️](https://github.com/trzsz), buy the author a drink 🍺 ? Thank you for your support!

tsshd/bus.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,16 @@ func sendBusMessage(command string, msg any) error {
6464
}
6565

6666
func trySendErrorMessage(format string, a ...any) {
67-
_ = sendBusMessage("error", ErrorMessage{fmt.Sprintf(format, a...)})
67+
done := make(chan struct{}, 1)
68+
go func() {
69+
defer close(done)
70+
_ = sendBusMessage("error", ErrorMessage{fmt.Sprintf(format, a...)})
71+
done <- struct{}{}
72+
}()
73+
select {
74+
case <-time.After(1 * time.Second):
75+
case <-done:
76+
}
6877
}
6978

7079
func handleBusEvent(stream net.Conn) {
@@ -93,11 +102,12 @@ func handleBusEvent(stream net.Conn) {
93102

94103
serving.Store(true)
95104

96-
if msg.Timeout > 0 {
97-
now := time.Now()
98-
lastAliveTime.Store(&now)
99-
go keepAlive(msg.Timeout, msg.Interval)
105+
if msg.Timeout <= 0 {
106+
msg.Timeout = 365 * 24 * time.Hour
100107
}
108+
now := time.Now()
109+
lastAliveTime.Store(&now)
110+
go keepAlive(msg.Timeout, msg.Interval)
101111

102112
for {
103113
command, err := RecvCommand(stream)
@@ -133,7 +143,7 @@ func handleUnknownEvent(stream net.Conn) error {
133143
}
134144

135145
func keepAlive(totalTimeout time.Duration, intervalTimeout time.Duration) {
136-
if intervalTimeout == 0 {
146+
if intervalTimeout <= 0 {
137147
intervalTimeout = min(totalTimeout/10, 10*time.Second)
138148
}
139149
go func() {

tsshd/main.go

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"os"
3131
"os/exec"
3232
"os/signal"
33+
"strconv"
3334
"strings"
3435
"syscall"
3536
"time"
@@ -40,26 +41,32 @@ const kTsshdVersion = "0.1.5"
4041
var exitChan = make(chan int, 1)
4142

4243
type tsshdArgs struct {
43-
Help bool
44-
Version bool
45-
KCP bool
46-
Proxy bool
47-
Port string
44+
Help bool
45+
Version bool
46+
KCP bool
47+
IPv4 bool
48+
IPv6 bool
49+
Proxy bool
50+
Port string
51+
ConnectTimeout time.Duration
4852
}
4953

5054
func printVersion() {
5155
fmt.Printf("trzsz sshd %s\n", kTsshdVersion)
5256
}
5357

5458
func printHelp() {
55-
fmt.Printf("usage: tsshd [-h] [-v] [--kcp] [--proxy] [--port low-high]\n\n" +
56-
"tsshd works with `tssh --udp`, just like mosh-server.\n\n" +
59+
fmt.Printf("usage: tsshd [-h] [-v] [--kcp] [--ipv4] [--ipv6] [--proxy] [--port low-high] [--connect-timeout t]\n\n" +
60+
"tsshd: trzsz-ssh(tssh) server that supports connection migration for roaming.\n\n" +
5761
"optional arguments:\n" +
5862
" -h, --help show this help message and exit\n" +
5963
" -v, --version show program's version number and exit\n" +
6064
" --kcp KCP protocol (default is QUIC protocol)\n" +
65+
" --ipv4 UDP only listens on IPv4, ignoring IPv6\n" +
66+
" --ipv6 UDP only listens on IPv6, ignoring IPv4\n" +
6167
" --proxy With UDP proxy for connection migration\n" +
62-
" --port low-high UDP port range that the tsshd listens on\n")
68+
" --port low-high UDP port range that the tsshd listens on\n" +
69+
" --connect-timeout t The timeout for tssh connecting to tsshd\n")
6370
}
6471

6572
func parseTsshdArgs() *tsshdArgs {
@@ -74,13 +81,24 @@ func parseTsshdArgs() *tsshdArgs {
7481
return args
7582
case "--kcp":
7683
args.KCP = true
84+
case "--ipv4":
85+
args.IPv4 = true
86+
case "--ipv6":
87+
args.IPv6 = true
7788
case "--proxy":
7889
args.Proxy = true
7990
case "--port":
8091
if i+1 < len(os.Args) && !strings.HasPrefix(os.Args[i+1], "-") {
8192
args.Port = os.Args[i+1]
8293
i++
8394
}
95+
case "--connect-timeout":
96+
if i+1 < len(os.Args) && !strings.HasPrefix(os.Args[i+1], "-") {
97+
if timeout, err := strconv.ParseUint(os.Args[i+1], 10, 32); err == nil {
98+
args.ConnectTimeout = time.Duration(timeout) * time.Second
99+
}
100+
i++
101+
}
84102
}
85103
}
86104
return args
@@ -164,8 +182,12 @@ func TsshdMain() int {
164182
}
165183

166184
go func() {
167-
// should be connected within 20 seconds
168-
time.Sleep(20 * time.Second)
185+
// should be connected in time
186+
connectTimeout := args.ConnectTimeout
187+
if connectTimeout <= 0 {
188+
connectTimeout = 10 * time.Second
189+
}
190+
time.Sleep(connectTimeout)
169191
if !serving.Load() {
170192
exitChan <- 1
171193
}

0 commit comments

Comments
 (0)