Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
后端镜像
Tabby
提交
e1f2e176
T
Tabby
项目概览
后端镜像
/
Tabby
通知
37
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
T
Tabby
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
e1f2e176
编写于
7月 10, 2021
作者:
E
Eugene Pankov
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
actually implement telnet protocol - fixes #4164
上级
f39b4c6d
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
181 addition
and
18 deletion
+181
-18
tabby-telnet/src/profiles.ts
tabby-telnet/src/profiles.ts
+29
-15
tabby-telnet/src/session.ts
tabby-telnet/src/session.ts
+152
-3
未找到文件。
tabby-telnet/src/profiles.ts
浏览文件 @
e1f2e176
...
@@ -12,22 +12,36 @@ export class TelnetProfilesService extends ProfileProvider {
...
@@ -12,22 +12,36 @@ export class TelnetProfilesService extends ProfileProvider {
settingsComponent
=
TelnetProfileSettingsComponent
settingsComponent
=
TelnetProfileSettingsComponent
async
getBuiltinProfiles
():
Promise
<
TelnetProfile
[]
>
{
async
getBuiltinProfiles
():
Promise
<
TelnetProfile
[]
>
{
return
[{
return
[
id
:
`telnet:template`
,
{
type
:
'
telnet
'
,
id
:
`telnet:template`
,
name
:
'
Telnet/socket connection
'
,
type
:
'
telnet
'
,
icon
:
'
fas fa-network-wired
'
,
name
:
'
Telnet session
'
,
options
:
{
icon
:
'
fas fa-network-wired
'
,
host
:
''
,
options
:
{
port
:
23
,
host
:
''
,
inputMode
:
'
readline
'
,
port
:
23
,
outputMode
:
null
,
inputMode
:
'
readline
'
,
inputNewlines
:
null
,
outputMode
:
null
,
outputNewlines
:
'
crlf
'
,
inputNewlines
:
null
,
outputNewlines
:
'
crlf
'
,
},
isBuiltin
:
true
,
isTemplate
:
true
,
},
{
id
:
`socket:template`
,
type
:
'
telnet
'
,
name
:
'
Raw socket connection
'
,
icon
:
'
fas fa-network-wired
'
,
options
:
{
host
:
''
,
port
:
1234
,
},
isBuiltin
:
true
,
isTemplate
:
true
,
},
},
isBuiltin
:
true
,
]
isTemplate
:
true
,
}]
}
}
async
getNewTabParameters
(
profile
:
Profile
):
Promise
<
NewTabParameters
<
TelnetTabComponent
>>
{
async
getNewTabParameters
(
profile
:
Profile
):
Promise
<
NewTabParameters
<
TelnetTabComponent
>>
{
...
...
tabby-telnet/src/session.ts
浏览文件 @
e1f2e176
...
@@ -16,12 +16,38 @@ export interface TelnetProfileOptions extends StreamProcessingOptions, LoginScri
...
@@ -16,12 +16,38 @@ export interface TelnetProfileOptions extends StreamProcessingOptions, LoginScri
port
?:
number
port
?:
number
}
}
enum
TelnetCommands
{
SUBOPTION_END
=
240
,
GA
=
249
,
SUBOPTION
=
250
,
WILL
=
251
,
WONT
=
252
,
DO
=
253
,
DONT
=
254
,
IAC
=
255
,
}
enum
TelnetOptions
{
ECHO
=
0x1
,
AUTH_OPTIONS
=
0x25
,
SUPPRESS_GO_AHEAD
=
0x03
,
TERMINAL_TYPE
=
0x18
,
NEGO_WINDOW_SIZE
=
0x1f
,
NEGO_TERMINAL_SPEED
=
0x20
,
STATUS
=
0x05
,
X_DISPLAY_LOCATION
=
0x23
,
}
export
class
TelnetSession
extends
BaseSession
{
export
class
TelnetSession
extends
BaseSession
{
get
serviceMessage$
():
Observable
<
string
>
{
return
this
.
serviceMessage
}
get
serviceMessage$
():
Observable
<
string
>
{
return
this
.
serviceMessage
}
private
serviceMessage
=
new
Subject
<
string
>
()
private
serviceMessage
=
new
Subject
<
string
>
()
private
socket
:
Socket
private
socket
:
Socket
private
streamProcessor
:
TerminalStreamProcessor
private
streamProcessor
:
TerminalStreamProcessor
private
telnetProtocol
=
false
private
echoEnabled
=
false
private
lastWidth
=
0
private
lastHeight
=
0
constructor
(
constructor
(
injector
:
Injector
,
injector
:
Injector
,
...
@@ -30,7 +56,7 @@ export class TelnetSession extends BaseSession {
...
@@ -30,7 +56,7 @@ export class TelnetSession extends BaseSession {
super
(
injector
.
get
(
LogService
).
create
(
`telnet-
${
profile
.
options
.
host
}
-
${
profile
.
options
.
port
}
`
))
super
(
injector
.
get
(
LogService
).
create
(
`telnet-
${
profile
.
options
.
host
}
-
${
profile
.
options
.
port
}
`
))
this
.
streamProcessor
=
new
TerminalStreamProcessor
(
profile
.
options
)
this
.
streamProcessor
=
new
TerminalStreamProcessor
(
profile
.
options
)
this
.
streamProcessor
.
outputToSession$
.
subscribe
(
data
=>
{
this
.
streamProcessor
.
outputToSession$
.
subscribe
(
data
=>
{
this
.
socket
.
write
(
data
)
this
.
socket
.
write
(
this
.
unescapeFF
(
data
)
)
})
})
this
.
streamProcessor
.
outputToTerminal$
.
subscribe
(
data
=>
{
this
.
streamProcessor
.
outputToTerminal$
.
subscribe
(
data
=>
{
this
.
emitOutput
(
data
)
this
.
emitOutput
(
data
)
...
@@ -38,6 +64,24 @@ export class TelnetSession extends BaseSession {
...
@@ -38,6 +64,24 @@ export class TelnetSession extends BaseSession {
this
.
setLoginScriptsOptions
(
profile
.
options
)
this
.
setLoginScriptsOptions
(
profile
.
options
)
}
}
unescapeFF
(
data
:
Buffer
):
Buffer
{
if
(
!
this
.
telnetProtocol
)
{
return
data
}
const
result
:
Buffer
[]
=
[]
while
(
data
.
includes
(
0xff
))
{
const
pos
=
data
.
indexOf
(
0xff
)
result
.
push
(
data
.
slice
(
0
,
pos
))
result
.
push
(
Buffer
.
from
([
0xff
,
0xff
]))
data
=
data
.
slice
(
pos
+
1
)
}
result
.
push
(
data
)
return
Buffer
.
concat
(
result
)
}
async
start
():
Promise
<
void
>
{
async
start
():
Promise
<
void
>
{
this
.
socket
=
new
Socket
()
this
.
socket
=
new
Socket
()
this
.
emitServiceMessage
(
`Connecting to
${
this
.
profile
.
options
.
host
}
`
)
this
.
emitServiceMessage
(
`Connecting to
${
this
.
profile
.
options
.
host
}
`
)
...
@@ -52,7 +96,7 @@ export class TelnetSession extends BaseSession {
...
@@ -52,7 +96,7 @@ export class TelnetSession extends BaseSession {
this
.
emitServiceMessage
(
'
Connection closed
'
)
this
.
emitServiceMessage
(
'
Connection closed
'
)
this
.
destroy
()
this
.
destroy
()
})
})
this
.
socket
.
on
(
'
data
'
,
data
=>
this
.
streamProcessor
.
feedFromSession
(
data
))
this
.
socket
.
on
(
'
data
'
,
data
=>
this
.
onData
(
data
))
this
.
socket
.
connect
(
this
.
profile
.
options
.
port
??
23
,
this
.
profile
.
options
.
host
,
()
=>
{
this
.
socket
.
connect
(
this
.
profile
.
options
.
port
??
23
,
this
.
profile
.
options
.
host
,
()
=>
{
this
.
emitServiceMessage
(
'
Connected
'
)
this
.
emitServiceMessage
(
'
Connected
'
)
this
.
open
=
true
this
.
open
=
true
...
@@ -68,10 +112,115 @@ export class TelnetSession extends BaseSession {
...
@@ -68,10 +112,115 @@ export class TelnetSession extends BaseSession {
this
.
logger
.
info
(
stripAnsi
(
msg
))
this
.
logger
.
info
(
stripAnsi
(
msg
))
}
}
onData
(
data
:
Buffer
):
void
{
if
(
!
this
.
telnetProtocol
&&
data
[
0
]
===
TelnetCommands
.
IAC
)
{
this
.
telnetProtocol
=
true
this
.
emitTelnet
(
TelnetCommands
.
DO
,
TelnetOptions
.
SUPPRESS_GO_AHEAD
)
this
.
emitTelnet
(
TelnetCommands
.
WILL
,
TelnetOptions
.
TERMINAL_TYPE
)
this
.
emitTelnet
(
TelnetCommands
.
WILL
,
TelnetOptions
.
NEGO_WINDOW_SIZE
)
}
if
(
this
.
telnetProtocol
)
{
data
=
this
.
processTelnetProtocol
(
data
)
}
this
.
streamProcessor
.
feedFromSession
(
data
)
}
emitTelnet
(
command
:
TelnetCommands
,
option
:
TelnetOptions
):
void
{
this
.
logger
.
debug
(
'
>
'
,
TelnetCommands
[
command
],
TelnetOptions
[
option
])
this
.
socket
.
write
(
Buffer
.
from
([
TelnetCommands
.
IAC
,
command
,
option
]))
}
emitTelnetSuboption
(
option
:
TelnetOptions
,
value
:
Buffer
):
void
{
this
.
logger
.
debug
(
'
>
'
,
'
SUBOPTION
'
,
TelnetOptions
[
option
],
value
)
this
.
socket
.
write
(
Buffer
.
from
([
TelnetCommands
.
IAC
,
TelnetCommands
.
SUBOPTION
,
option
,
...
value
,
TelnetCommands
.
IAC
,
TelnetCommands
.
SUBOPTION_END
,
]))
}
processTelnetProtocol
(
data
:
Buffer
):
Buffer
{
while
(
data
.
length
)
{
if
(
data
[
0
]
===
TelnetCommands
.
IAC
)
{
const
command
=
data
[
1
]
const
commandName
=
TelnetCommands
[
command
]
const
option
=
data
[
2
]
const
optionName
=
TelnetOptions
[
option
]
if
(
command
===
TelnetCommands
.
IAC
)
{
data
=
data
.
slice
(
1
)
break
}
data
=
data
.
slice
(
3
)
this
.
logger
.
debug
(
'
<
'
,
commandName
||
command
,
optionName
||
option
)
if
(
command
===
TelnetCommands
.
WILL
)
{
if
([
TelnetOptions
.
SUPPRESS_GO_AHEAD
,
TelnetOptions
.
ECHO
,
].
includes
(
option
))
{
this
.
emitTelnet
(
TelnetCommands
.
DO
,
option
)
}
else
{
this
.
logger
.
debug
(
'
(!) Unhandled option
'
)
this
.
emitTelnet
(
TelnetCommands
.
DONT
,
option
)
}
}
if
(
command
===
TelnetCommands
.
DO
)
{
if
(
option
===
TelnetOptions
.
NEGO_WINDOW_SIZE
)
{
this
.
resize
(
0
,
0
)
}
else
if
(
option
===
TelnetOptions
.
ECHO
)
{
this
.
echoEnabled
=
true
this
.
emitTelnet
(
TelnetCommands
.
WILL
,
option
)
}
else
if
(
option
===
TelnetOptions
.
TERMINAL_TYPE
)
{
this
.
emitTelnetSuboption
(
option
,
Buffer
.
from
([
0
,
...
Buffer
.
from
(
'
XTERM-256COLOR
'
)]))
}
else
{
this
.
logger
.
debug
(
'
(!) Unhandled option
'
)
this
.
emitTelnet
(
TelnetCommands
.
WONT
,
option
)
}
}
if
(
command
===
TelnetCommands
.
DONT
)
{
if
(
option
===
TelnetOptions
.
ECHO
)
{
this
.
echoEnabled
=
false
this
.
emitTelnet
(
TelnetCommands
.
WONT
,
option
)
}
else
{
this
.
logger
.
debug
(
'
(!) Unhandled option
'
)
this
.
emitTelnet
(
TelnetCommands
.
WILL
,
option
)
}
}
if
(
command
===
TelnetCommands
.
SUBOPTION
)
{
const
endIndex
=
data
.
indexOf
(
TelnetCommands
.
IAC
)
const
optionValue
=
data
.
slice
(
0
,
endIndex
)
this
.
logger
.
debug
(
'
<
'
,
commandName
||
command
,
optionName
||
option
,
optionValue
)
data
=
data
.
slice
(
endIndex
+
2
)
}
}
else
{
return
data
}
}
return
data
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
// eslint-disable-next-line @typescript-eslint/no-empty-function
resize
(
_w
:
number
,
_h
:
number
):
void
{
}
resize
(
w
:
number
,
h
:
number
):
void
{
if
(
w
&&
h
)
{
this
.
lastWidth
=
w
this
.
lastHeight
=
h
}
if
(
this
.
lastWidth
&&
this
.
lastHeight
&&
this
.
telnetProtocol
)
{
this
.
emitTelnetSuboption
(
TelnetOptions
.
NEGO_WINDOW_SIZE
,
Buffer
.
from
([
this
.
lastWidth
>>
8
,
this
.
lastWidth
&
0xff
,
this
.
lastHeight
>>
8
,
this
.
lastHeight
&
0xff
,
]))
}
}
write
(
data
:
Buffer
):
void
{
write
(
data
:
Buffer
):
void
{
if
(
this
.
echoEnabled
)
{
this
.
emitOutput
(
data
)
}
this
.
streamProcessor
.
feedFromTerminal
(
data
)
this
.
streamProcessor
.
feedFromTerminal
(
data
)
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录