mirror of
https://github.com/apernet/OpenGFW.git
synced 2024-11-14 22:39:26 +08:00
148 lines
3.6 KiB
Go
148 lines
3.6 KiB
Go
package tcp
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/apernet/OpenGFW/analyzer"
|
|
"github.com/apernet/OpenGFW/analyzer/utils"
|
|
)
|
|
|
|
var _ analyzer.TCPAnalyzer = (*SSHAnalyzer)(nil)
|
|
|
|
type SSHAnalyzer struct{}
|
|
|
|
func (a *SSHAnalyzer) Name() string {
|
|
return "ssh"
|
|
}
|
|
|
|
func (a *SSHAnalyzer) Limit() int {
|
|
return 1024
|
|
}
|
|
|
|
func (a *SSHAnalyzer) NewTCP(info analyzer.TCPInfo, logger analyzer.Logger) analyzer.TCPStream {
|
|
return newSSHStream(logger)
|
|
}
|
|
|
|
type sshStream struct {
|
|
logger analyzer.Logger
|
|
|
|
clientBuf *utils.ByteBuffer
|
|
clientMap analyzer.PropMap
|
|
clientUpdated bool
|
|
clientLSM *utils.LinearStateMachine
|
|
clientDone bool
|
|
|
|
serverBuf *utils.ByteBuffer
|
|
serverMap analyzer.PropMap
|
|
serverUpdated bool
|
|
serverLSM *utils.LinearStateMachine
|
|
serverDone bool
|
|
}
|
|
|
|
func newSSHStream(logger analyzer.Logger) *sshStream {
|
|
s := &sshStream{logger: logger, clientBuf: &utils.ByteBuffer{}, serverBuf: &utils.ByteBuffer{}}
|
|
s.clientLSM = utils.NewLinearStateMachine(
|
|
s.parseClientExchangeLine,
|
|
)
|
|
s.serverLSM = utils.NewLinearStateMachine(
|
|
s.parseServerExchangeLine,
|
|
)
|
|
return s
|
|
}
|
|
|
|
func (s *sshStream) Feed(rev, start, end bool, skip int, data []byte) (u *analyzer.PropUpdate, done bool) {
|
|
if skip != 0 {
|
|
return nil, true
|
|
}
|
|
if len(data) == 0 {
|
|
return nil, false
|
|
}
|
|
var update *analyzer.PropUpdate
|
|
var cancelled bool
|
|
if rev {
|
|
s.serverBuf.Append(data)
|
|
s.serverUpdated = false
|
|
cancelled, s.serverDone = s.serverLSM.Run()
|
|
if s.serverUpdated {
|
|
update = &analyzer.PropUpdate{
|
|
Type: analyzer.PropUpdateMerge,
|
|
M: analyzer.PropMap{"server": s.serverMap},
|
|
}
|
|
s.serverUpdated = false
|
|
}
|
|
} else {
|
|
s.clientBuf.Append(data)
|
|
s.clientUpdated = false
|
|
cancelled, s.clientDone = s.clientLSM.Run()
|
|
if s.clientUpdated {
|
|
update = &analyzer.PropUpdate{
|
|
Type: analyzer.PropUpdateMerge,
|
|
M: analyzer.PropMap{"client": s.clientMap},
|
|
}
|
|
s.clientUpdated = false
|
|
}
|
|
}
|
|
return update, cancelled || (s.clientDone && s.serverDone)
|
|
}
|
|
|
|
// parseExchangeLine parses the SSH Protocol Version Exchange string.
|
|
// See RFC 4253, section 4.2.
|
|
// "SSH-protoversion-softwareversion SP comments CR LF"
|
|
// The "comments" part (along with the SP) is optional.
|
|
func (s *sshStream) parseExchangeLine(buf *utils.ByteBuffer) (utils.LSMAction, analyzer.PropMap) {
|
|
// Find the end of the line
|
|
line, ok := buf.GetUntil([]byte("\r\n"), true, true)
|
|
if !ok {
|
|
// No end of line yet, but maybe we just need more data
|
|
return utils.LSMActionPause, nil
|
|
}
|
|
if !strings.HasPrefix(string(line), "SSH-") {
|
|
// Not SSH
|
|
return utils.LSMActionCancel, nil
|
|
}
|
|
fields := strings.Fields(string(line[:len(line)-2])) // Strip \r\n
|
|
if len(fields) < 1 || len(fields) > 2 {
|
|
// Invalid line
|
|
return utils.LSMActionCancel, nil
|
|
}
|
|
sshFields := strings.SplitN(fields[0], "-", 3)
|
|
if len(sshFields) != 3 {
|
|
// Invalid SSH version format
|
|
return utils.LSMActionCancel, nil
|
|
}
|
|
sMap := analyzer.PropMap{
|
|
"protocol": sshFields[1],
|
|
"software": sshFields[2],
|
|
}
|
|
if len(fields) == 2 {
|
|
sMap["comments"] = fields[1]
|
|
}
|
|
return utils.LSMActionNext, sMap
|
|
}
|
|
|
|
func (s *sshStream) parseClientExchangeLine() utils.LSMAction {
|
|
action, sMap := s.parseExchangeLine(s.clientBuf)
|
|
if action == utils.LSMActionNext {
|
|
s.clientMap = sMap
|
|
s.clientUpdated = true
|
|
}
|
|
return action
|
|
}
|
|
|
|
func (s *sshStream) parseServerExchangeLine() utils.LSMAction {
|
|
action, sMap := s.parseExchangeLine(s.serverBuf)
|
|
if action == utils.LSMActionNext {
|
|
s.serverMap = sMap
|
|
s.serverUpdated = true
|
|
}
|
|
return action
|
|
}
|
|
|
|
func (s *sshStream) Close(limited bool) *analyzer.PropUpdate {
|
|
s.clientBuf.Reset()
|
|
s.serverBuf.Reset()
|
|
s.clientMap = nil
|
|
s.serverMap = nil
|
|
return nil
|
|
}
|