-
Notifications
You must be signed in to change notification settings - Fork 0
/
mosh-tun
executable file
·284 lines (251 loc) · 6.94 KB
/
mosh-tun
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
#!/bin/bash
# A wrapper to establish mosh connection to remote machine with proxy chain.
#
# TODO support more mosh-server initialization methods: nc server, curl, and
# - [X] nc daemon
# - [X] ssh
#
# TODO Global relay registry
VERSION=20200425
### Sanity check
# For use within MobaXterm, probably works also in vanilla Cygwin
# Dependencies:
# cygpath: for file paths transfomration between Linux and Unix
if [ -z "$MOBANOACL" ]; then
echo "ERROR: used only in MobaXterm"
exit -1
fi
GOST=$(which gost)
MUSTACHE=$(which mustache)
for dep in "$GOST" "$MUSTACHE"; do
if [[ -z $dep ]]; then
echo "ERROR: dependence $dep not met."
exit 1
fi
done
### Debug
DEBUG=${DEBUG:-0}
if [[ "$DEBUG" == 1 ]]; then
# verbose+breakpoint mode (debug)
set -x
export PS4='+[\t${LINENO}]: '
__BEGIN_DEBUG__="trap read debug"
__END_DEBUG__="trap - debug"
fi
### Info (static)
TOP=$(dirname $(realpath ${0}))
PROGRAM=$(basename ${0})
SESSION_NAME="$PROGRAM-$(date +%s)-PID$$"
SESSION_LOG="$TOP/log/$SESSION_NAME.log"
echo > $SESSION_LOG # truncate log
LOGGER="echo $(date '+%m/%d/%Y-%H:%M:%S'): "
log(){
$LOGGER "$*" | tee -a "$SESSION_LOG"
}
### Routines
# Get the first available port: 50000+
getport(){
base=${1:-50000}
i=0
while :
do
let port=$base+$i
if [ -z "$(netstat -ano | grep $port)" ]; then
# scanning might be slower
break
fi
let i++
done
echo $port
}
# generate gost conf
# @return: generated gost conf path
# @params:
# $1: remote host
# $2: local relay port
# $3: mosh port
generate_gost_conf(){
# Will relay both ssh and mosh udp traffic
local rhost=$1
local local_relay_port=$2
local mosh_port=$3
# gost for windows only recognizes windows-style path
local gost_conf_tpl=$(cygpath -m "$TOP/configs/tunnel.json.mustache")
local ds_name=$(basename "$RHOST_DS" .yml)
local gost_conf=$(cygpath -m "$TOP/log/$ds_name.json")
cat "$RHOST_DS" - <<END | $MUSTACHE "$gost_conf_tpl" > "$gost_conf"
# extra line break to avoid no newline at the end of the ds file
local_port: $local_relay_port
mosh_server_port: $mosh_port
END
echo "$gost_conf"
}
GOST_RELAY_PID=
# (re)start gost relay
# @return: GOST_RELAY_PID
# @params:
# $1: remote host
# $2: local relay port
# $3: mosh port
restart_gost_relay(){
local rhost=$1
local local_relay_port=$2
local mosh_port=$3
local gost_conf=$(generate_gost_conf "$rhost" "$local_relay_port" "$mosh_port")
local gost_log=$TOP/log/$SESSION_NAME-relay.log
if [[ -n $GOST_RELAY_PID ]]; then
kill "$GOST_RELAY_PID"
fi
sleep 1
"$GOST" -C "$gost_conf" &>>"$gost_log" &
GOST_RELAY_PID="$!"
echo "$GOST_RELAY_PID"
}
sotp_gost_relay(){
if [[ -n $GOST_RELAY_PID ]]; then
kill "$GOST_RELAY_PID"
log "[main] udp relay closed. PID ${GOST_RELAY_PID}."
fi
}
print_help(){
cat <<END
Usage: $PROGRAM [options] [<host>]
host must have a corresponding 'ds-*.yml' file under configs/
options: certain options are only used with specific methods
-h: print help message.
-l: list available hosts, i.e. hosts with configuration files.
-v: print version info.
-m: method to start mosh-server, default to nc.
Supported methods: nc, ssh
-t: [nc] seconds (default 10) to wait for mosh-server to start.
-c: specify tunnel settings, if omitted, default to configs/ds-<method>-<host>.yml
END
}
print_host_list(){
echo 'Available (method, host) (configs/ds-<method>-<rhost>.yml):'
for ds in "$TOP/configs/"ds-*.yml; do
ds_desc=$(basename "$ds" .yml | sed 's/^ds-//; s/-/:\t\t/')
echo "- $ds_desc"
done
}
if [[ $# -eq 0 ]]; then
print_help
exit 0
fi
while getopts ":hlvt:c:m:" opt; do
case "$opt" in
h)
print_help
exit 0
;;
l)
# TODO use absolute path
print_host_list
exit 0
;;
v)
echo "Version: $VERSION"
exit 0
;;
t)
NC_TIMEOUT=$OPTARG
;;
c)
RHOST_DS=$OPTARG
if [[ ! -r $RHOST_DS ]]; then
echo "ERROR: $RHOST_DS does NOT exist."
exit 1
fi
;;
m)
METHOD=$OPTARG
if [[ ! $METHOD =~ ssh|nc ]]; then
echo "ERROR: method $METHOD not supported."
exit 1
fi
;;
:)
print_help
exit 1
;;
esac
done
shift $((OPTIND - 1))
METHOD=${METHOD:-nc}
RHOST=$1
# Host data source
RHOST_DS=${RHOST_DS:-$TOP/configs/ds-$METHOD-$RHOST.yml}
if [[ ! -e $RHOST_DS ]]; then
echo "ERROR: Host $RHOST is not available. Abort."
print_host_list
exit 1
fi
### relay
RELAY_PORT_L=$(getport)
log "[gost] setting up relay tunnel [:$RELAY_PORT_L -> $RHOST:]..."
# 60000 is the default mosh-server port
MOSH_PORT=60000
GOST_RELAY_PID=$(restart_gost_relay "$RHOST" "$RELAY_PORT_L" "$MOSH_PORT")
if [[ -z $GOST_RELAY_PID ]]; then
echo "ERROR: fail to establish relay connections."
exit 1
fi
log "[gost] established relay tunnel [:$RELAY_PORT_L -> $RHOST:] PID: $GOST_RELAY_PID"
### mosh-server
_server_nc_daemon(){
# nc will quit almost immediately after the stdin is closed.
# TODO Remedies include a fifo file or just use curl with a http server
local timeout=${NC_TIMEOUT:-10}
(echo 'mosh-server'; sleep "$timeout") | nc 127.0.0.1 "$RELAY_PORT_L"
}
_server_sshd(){
local ssh_config_tpl=$(cygpath -m "$TOP/configs/_ssh_config.mustache")
local ssh_config=$TOP/log/_ssh_config-$RHOST
cat "$RHOST_DS" | "$MUSTACHE" "$ssh_config_tpl" > "$ssh_config"
ssh -F "$ssh_config" 127.0.0.1 -p "$RELAY_PORT_L" mosh-server
}
create_mosh_session(){
local method=$1
case "$method" in
nc)
_server_nc_daemon
;;
ssh)
_server_sshd
;;
*)
echo "ERROR: method not supported: $method"
stop_gost_relay
exit 1
;;
esac
}
$__BEGIN_DEBUG__
log "[main] starting mosh-server at $RHOST ..."
MOSH_CONNECT=$( create_mosh_session $METHOD | awk -e '/MOSH CONNECT/{ print $3 " " $4 }')
if [[ -z "$MOSH_CONNECT" ]]; then
log "[main] Failed to start mosh-server. Abort"
sotp_gost_relay
exit 1
fi
read MOSH_PORT MOSH_KEY <<<"$MOSH_CONNECT"
log "[mosh] Key: $MOSH_KEY; Server Port: $MOSH_PORT; Client Port: $RELAY_PORT_L"
if [[ $MOSH_PORT != 60000 ]]; then
GOST_RELAY_PID=$(restart_gost_relay "$RHOST" "$RELAY_PORT_L" "$MOSH_PORT")
fi
### mosh-client
log "[mosh] starting mosh-client ... "
MOSH_KEY="$MOSH_KEY" mosh-client 127.0.0.1 "$RELAY_PORT_L"
### Clean up
trap_ctrlc (){
echo "Abort upon user request (Ctrl-C caught)...performing clean up"
sotp_gost_relay
echo "Exit. Bye."
exit 0
}
# initialise trap to call trap_ctrlc function
# when signal 2 (SIGINT) is received
trap "trap_ctrlc" 2
log "[main] mosh-client exited... cleaning up"
sotp_gost_relay
log "Done. Bye."