mirror of
https://github.com/mjl-/mox.git
synced 2024-12-26 08:23:48 +03:00
for imap/smtp syntax errors, only echo the remaining buffer if the connection is authenticated
This commit is contained in:
parent
e413c906b1
commit
f9eae88aba
6 changed files with 55 additions and 18 deletions
|
@ -1,6 +1,7 @@
|
||||||
package imapserver
|
package imapserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,13 +37,14 @@ func xserverErrorf(format string, args ...any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type syntaxError struct {
|
type syntaxError struct {
|
||||||
line string // Optional line to write before BAD result. For untagged response. CRLF will be added.
|
line string // Optional line to write before BAD result. For untagged response. CRLF will be added.
|
||||||
code string // Optional result code (between []) to write in BAD result.
|
code string // Optional result code (between []) to write in BAD result.
|
||||||
err error // BAD response message.
|
errmsg string // BAD response message.
|
||||||
|
err error // Typically with same info as errmsg, but sometimes more.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e syntaxError) Error() string {
|
func (e syntaxError) Error() string {
|
||||||
s := "bad syntax: " + e.err.Error()
|
s := "bad syntax: " + e.errmsg
|
||||||
if e.code != "" {
|
if e.code != "" {
|
||||||
s += " [" + e.code + "]"
|
s += " [" + e.code + "]"
|
||||||
}
|
}
|
||||||
|
@ -51,5 +53,7 @@ func (e syntaxError) Error() string {
|
||||||
func (e syntaxError) Unwrap() error { return e.err }
|
func (e syntaxError) Unwrap() error { return e.err }
|
||||||
|
|
||||||
func xsyntaxErrorf(format string, args ...any) {
|
func xsyntaxErrorf(format string, args ...any) {
|
||||||
panic(syntaxError{"", "", fmt.Errorf(format, args...)})
|
errmsg := fmt.Sprintf(format, args...)
|
||||||
|
err := errors.New(errmsg)
|
||||||
|
panic(syntaxError{"", "", errmsg, err})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package imapserver
|
package imapserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -74,11 +75,20 @@ func newParser(s string, conn *conn) *parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) xerrorf(format string, args ...any) {
|
func (p *parser) xerrorf(format string, args ...any) {
|
||||||
var context string
|
var err error
|
||||||
|
errmsg := fmt.Sprintf(format, args...)
|
||||||
|
remaining := fmt.Sprintf("remaining %q", p.orig[p.o:])
|
||||||
if len(p.contexts) > 0 {
|
if len(p.contexts) > 0 {
|
||||||
context = strings.Join(p.contexts, ",")
|
remaining += ", context " + strings.Join(p.contexts, ",")
|
||||||
}
|
}
|
||||||
panic(syntaxError{"", "", fmt.Errorf("%s (%sremaining data %q)", fmt.Sprintf(format, args...), context, p.orig[p.o:])})
|
remaining = " (" + remaining + ")"
|
||||||
|
if p.conn.account != nil {
|
||||||
|
errmsg += remaining
|
||||||
|
err = errors.New(errmsg)
|
||||||
|
} else {
|
||||||
|
err = errors.New(errmsg + remaining)
|
||||||
|
}
|
||||||
|
panic(syntaxError{"", "", errmsg, err})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) context(s string) func() {
|
func (p *parser) context(s string) func() {
|
||||||
|
@ -724,7 +734,8 @@ func (p *parser) xliteralSize(maxSize int64, lit8 bool) (size int64, sync bool)
|
||||||
if maxSize > 0 && size > maxSize {
|
if maxSize > 0 && size > maxSize {
|
||||||
// ../rfc/7888:249
|
// ../rfc/7888:249
|
||||||
line := fmt.Sprintf("* BYE [ALERT] Max literal size %d is larger than allowed %d in this context", size, maxSize)
|
line := fmt.Sprintf("* BYE [ALERT] Max literal size %d is larger than allowed %d in this context", size, maxSize)
|
||||||
panic(syntaxError{line, "TOOBIG", fmt.Errorf("literal too big")})
|
err := errors.New("literal too big")
|
||||||
|
panic(syntaxError{line, "TOOBIG", err.Error(), err})
|
||||||
}
|
}
|
||||||
|
|
||||||
sync = !p.take("+")
|
sync = !p.take("+")
|
||||||
|
|
|
@ -760,14 +760,21 @@ func (c *conn) command() {
|
||||||
c.writelinef("* BYE please try again speaking imap")
|
c.writelinef("* BYE please try again speaking imap")
|
||||||
panic(errIO)
|
panic(errIO)
|
||||||
}
|
}
|
||||||
c.log.Debugx("imap command syntax error", err, logFields...)
|
c.log.Debugx("imap command syntax error", sxerr.err, logFields...)
|
||||||
c.log.Info("imap syntax error", mlog.Field("lastline", c.lastLine))
|
c.log.Info("imap syntax error", mlog.Field("lastline", c.lastLine))
|
||||||
fatal := strings.HasSuffix(c.lastLine, "+}")
|
fatal := strings.HasSuffix(c.lastLine, "+}")
|
||||||
if fatal {
|
if fatal {
|
||||||
err := c.conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
|
err := c.conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
|
||||||
c.log.Check(err, "setting write deadline")
|
c.log.Check(err, "setting write deadline")
|
||||||
}
|
}
|
||||||
c.bwriteresultf("%s BAD %s unrecognized syntax/command: %v", tag, cmd, err)
|
if sxerr.line != "" {
|
||||||
|
c.bwritelinef("%s", sxerr.line)
|
||||||
|
}
|
||||||
|
code := ""
|
||||||
|
if sxerr.code != "" {
|
||||||
|
code = "[" + sxerr.code + "] "
|
||||||
|
}
|
||||||
|
c.bwriteresultf("%s BAD %s%s unrecognized syntax/command: %v", tag, code, cmd, sxerr.errmsg)
|
||||||
if fatal {
|
if fatal {
|
||||||
c.xflush()
|
c.xflush()
|
||||||
panic(fmt.Errorf("aborting connection after syntax error for command with non-sync literal: %w", errProtocol))
|
panic(fmt.Errorf("aborting connection after syntax error for command with non-sync literal: %w", errProtocol))
|
||||||
|
@ -1618,7 +1625,7 @@ func (c *conn) cmdAuthenticate(tag, cmd string, p *parser) {
|
||||||
c0 := xreadInitial()
|
c0 := xreadInitial()
|
||||||
ss, err := scram.NewServer(h, c0)
|
ss, err := scram.NewServer(h, c0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xsyntaxErrorf("starting scram: %w", err)
|
xsyntaxErrorf("starting scram: %s", err)
|
||||||
}
|
}
|
||||||
c.log.Debug("scram auth", mlog.Field("authentication", ss.Authentication))
|
c.log.Debug("scram auth", mlog.Field("authentication", ss.Authentication))
|
||||||
acc, _, err := store.OpenEmail(ss.Authentication)
|
acc, _, err := store.OpenEmail(ss.Authentication)
|
||||||
|
|
|
@ -8,23 +8,26 @@ import (
|
||||||
|
|
||||||
func xcheckf(err error, format string, args ...any) {
|
func xcheckf(err error, format string, args ...any) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(smtpError{smtp.C451LocalErr, smtp.SeSys3Other0, fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err), true, false})
|
err := fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err)
|
||||||
|
panic(smtpError{smtp.C451LocalErr, smtp.SeSys3Other0, err.Error(), err, true, false})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type smtpError struct {
|
type smtpError struct {
|
||||||
code int
|
code int
|
||||||
secode string
|
secode string
|
||||||
err error
|
errmsg string // Sent in response.
|
||||||
|
err error // If set, used in logging. Typically has same information as errmsg.
|
||||||
printStack bool
|
printStack bool
|
||||||
userError bool // If this is an error on the user side, which causes logging at a lower level.
|
userError bool // If this is an error on the user side, which causes logging at a lower level.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e smtpError) Error() string { return e.err.Error() }
|
func (e smtpError) Error() string { return e.errmsg }
|
||||||
func (e smtpError) Unwrap() error { return e.err }
|
func (e smtpError) Unwrap() error { return e.err }
|
||||||
|
|
||||||
func xsmtpErrorf(code int, secode string, userError bool, format string, args ...any) {
|
func xsmtpErrorf(code int, secode string, userError bool, format string, args ...any) {
|
||||||
panic(smtpError{code, secode, fmt.Errorf(format, args...), false, userError})
|
err := fmt.Errorf(format, args...)
|
||||||
|
panic(smtpError{code, secode, err.Error(), err, false, userError})
|
||||||
}
|
}
|
||||||
|
|
||||||
func xsmtpServerErrorf(codes codes, format string, args ...any) {
|
func xsmtpServerErrorf(codes codes, format string, args ...any) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package smtpserver
|
package smtpserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -40,8 +41,19 @@ func newParser(s string, smtputf8 bool, conn *conn) *parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) xerrorf(format string, args ...any) {
|
func (p *parser) xerrorf(format string, args ...any) {
|
||||||
|
// For submission, send the remaining unparsed line. Otherwise, only log it.
|
||||||
|
var err error
|
||||||
|
errmsg := "bad syntax: " + fmt.Sprintf(format, args...)
|
||||||
|
remaining := fmt.Sprintf(" (remaining %q)", p.orig[p.o:])
|
||||||
|
if p.conn.account != nil {
|
||||||
|
errmsg += remaining
|
||||||
|
err = errors.New(errmsg)
|
||||||
|
} else {
|
||||||
|
err = errors.New(errmsg + remaining)
|
||||||
|
}
|
||||||
|
|
||||||
// ../rfc/5321:2377
|
// ../rfc/5321:2377
|
||||||
xsmtpUserErrorf(smtp.C501BadParamSyntax, smtp.SeProto5Syntax2, "%s (remaining: %q)", fmt.Sprintf(format, args...), p.orig[p.o:])
|
panic(smtpError{smtp.C501BadParamSyntax, smtp.SeProto5Syntax2, errmsg, err, false, true})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) xutf8localparterrorf() {
|
func (p *parser) xutf8localparterrorf() {
|
||||||
|
|
|
@ -669,7 +669,7 @@ func command(c *conn) {
|
||||||
|
|
||||||
var serr smtpError
|
var serr smtpError
|
||||||
if errors.As(err, &serr) {
|
if errors.As(err, &serr) {
|
||||||
c.writecodeline(serr.code, serr.secode, fmt.Sprintf("%s (%s)", serr.err, mox.ReceivedID(c.cid)), serr.err)
|
c.writecodeline(serr.code, serr.secode, fmt.Sprintf("%s (%s)", serr.errmsg, mox.ReceivedID(c.cid)), serr.err)
|
||||||
if serr.printStack {
|
if serr.printStack {
|
||||||
debug.PrintStack()
|
debug.PrintStack()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue