/
command.go
101 lines (83 loc) · 2.4 KB
/
command.go
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
package session
import (
"bytes"
"context"
"errors"
"github.com/ProtonMail/gluon/async"
"github.com/ProtonMail/gluon/imap/command"
"github.com/ProtonMail/gluon/internal/response"
"github.com/ProtonMail/gluon/logging"
"github.com/ProtonMail/gluon/reporter"
"github.com/ProtonMail/gluon/rfcparser"
"github.com/sirupsen/logrus"
)
type commandResult struct {
command command.Command
err error
}
func (s *Session) startCommandReader(ctx context.Context) <-chan commandResult {
cmdCh := make(chan commandResult)
async.GoAnnotated(ctx, s.panicHandler, func(ctx context.Context) {
defer close(cmdCh)
tlsHeaders := [][]byte{
{0x16, 0x03, 0x01}, // 1.0
{0x16, 0x03, 0x02}, // 1.1
{0x16, 0x03, 0x03}, // 1.2
{0x16, 0x03, 0x04}, // 1.3
{0x16, 0x00, 0x00}, // 0.0
}
parser := command.NewParserWithLiteralContinuationCb(s.scanner, func() error { return response.Continuation().Send(s) })
for {
s.inputCollector.Reset()
cmd, err := parser.Parse()
s.logIncoming(string(s.inputCollector.Bytes()))
if err != nil {
var parserError *rfcparser.Error
if !errors.As(err, &parserError) {
return
}
if parserError.IsEOF() {
return
}
if err := parser.ConsumeInvalidInput(); err != nil {
return
}
bytesRead := s.inputCollector.Bytes()
// check if we are receiving raw TLS requests, if so skip.
for _, tlsHeader := range tlsHeaders {
if bytes.HasPrefix(bytesRead, tlsHeader) {
logrus.Errorf("TLS Handshake detected while not running with TLS/SSL")
return
}
}
logrus.WithError(err).WithField("type", parser.LastParsedCommand()).Error("Failed to parse IMAP command")
reporter.MessageWithContext(ctx,
"Failed to parse IMAP command",
reporter.Context{"error": err, "cmd": parser.LastParsedCommand()},
)
} else {
logrus.Debug(cmd.SanitizedString())
}
switch c := cmd.Payload.(type) {
case *command.StartTLS:
// TLS needs to be handled here to ensure that next command read is over the TLS connection.
if err = s.handleStartTLS(cmd.Tag, c); err != nil {
logrus.WithError(err).Error("Cannot upgrade connection")
return
} else {
continue
}
}
select {
case cmdCh <- commandResult{command: cmd, err: err}:
// ...
case <-ctx.Done():
return
}
}
}, logging.Labels{
"Action": "Reading commands",
"SessionID": s.sessionID,
})
return cmdCh
}