mirror of
https://gitlab.com/ric_harvey/MailHog.git
synced 2024-11-23 22:34:04 +00:00
Fix #12 - support invalid MAIL/RCPT syntax
This commit is contained in:
parent
ba6490cc86
commit
9c2a484d6b
2 changed files with 61 additions and 18 deletions
|
@ -58,6 +58,15 @@ type Protocol struct {
|
|||
// parameters are valid, otherwise false. If nil, all authentication
|
||||
// attempts will be accepted.
|
||||
ValidateAuthenticationHandler func(mechanism string, args ...string) (errorReply *Reply, ok bool)
|
||||
|
||||
// RejectBrokenRCPTSyntax controls whether the protocol accepts technically
|
||||
// invalid syntax for the RCPT command. Set to true, the RCPT syntax requires
|
||||
// no space between `TO:` and the opening `<`
|
||||
RejectBrokenRCPTSyntax bool
|
||||
// RejectBrokenMAILSyntax controls whether the protocol accepts technically
|
||||
// invalid syntax for the MAIL command. Set to true, the MAIL syntax requires
|
||||
// no space between `FROM:` and the opening `<`
|
||||
RejectBrokenMAILSyntax bool
|
||||
}
|
||||
|
||||
// NewProtocol returns a new SMTP state machine in INVALID state
|
||||
|
@ -256,7 +265,7 @@ func (proto *Protocol) Command(command *Command) (reply *Reply) {
|
|||
}
|
||||
case "MAIL":
|
||||
proto.logf("Got MAIL command, switching to RCPT state")
|
||||
from, err := ParseMAIL(command.args)
|
||||
from, err := proto.ParseMAIL(command.args)
|
||||
if err != nil {
|
||||
return ReplyError(err)
|
||||
}
|
||||
|
@ -281,7 +290,7 @@ func (proto *Protocol) Command(command *Command) (reply *Reply) {
|
|||
switch command.verb {
|
||||
case "RCPT":
|
||||
proto.logf("Got RCPT command")
|
||||
to, err := ParseRCPT(command.args)
|
||||
to, err := proto.ParseRCPT(command.args)
|
||||
if err != nil {
|
||||
return ReplyError(err)
|
||||
}
|
||||
|
@ -328,8 +337,13 @@ func (proto *Protocol) EHLO(args string) (reply *Reply) {
|
|||
}
|
||||
|
||||
// ParseMAIL returns the forward-path from a MAIL command argument
|
||||
func ParseMAIL(mail string) (string, error) {
|
||||
r := regexp.MustCompile("(?i:From):<([^>]+)>")
|
||||
func (proto *Protocol) ParseMAIL(mail string) (string, error) {
|
||||
var r *regexp.Regexp
|
||||
if proto.RejectBrokenMAILSyntax {
|
||||
r = regexp.MustCompile("(?i:From):<([^>]+)>")
|
||||
} else {
|
||||
r = regexp.MustCompile("(?i:From):\\s*<([^>]+)>")
|
||||
}
|
||||
match := r.FindStringSubmatch(mail)
|
||||
if len(match) != 2 {
|
||||
return "", errors.New("Invalid syntax in MAIL command")
|
||||
|
@ -338,8 +352,13 @@ func ParseMAIL(mail string) (string, error) {
|
|||
}
|
||||
|
||||
// ParseRCPT returns the return-path from a RCPT command argument
|
||||
func ParseRCPT(rcpt string) (string, error) {
|
||||
r := regexp.MustCompile("(?i:To):<([^>]+)>")
|
||||
func (proto *Protocol) ParseRCPT(rcpt string) (string, error) {
|
||||
var r *regexp.Regexp
|
||||
if proto.RejectBrokenRCPTSyntax {
|
||||
r = regexp.MustCompile("(?i:To):<([^>]+)>")
|
||||
} else {
|
||||
r = regexp.MustCompile("(?i:To):\\s*<([^>]+)>")
|
||||
}
|
||||
match := r.FindStringSubmatch(rcpt)
|
||||
if len(match) != 2 {
|
||||
return "", errors.New("Invalid syntax in RCPT command")
|
||||
|
|
|
@ -511,28 +511,40 @@ func TestQUIT(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestParseMAIL(t *testing.T) {
|
||||
proto := NewProtocol()
|
||||
Convey("ParseMAIL should parse MAIL command arguments", t, func() {
|
||||
m, err := ParseMAIL("FROM:<oink@mailhog.example>")
|
||||
m, err := proto.ParseMAIL("FROM:<oink@mailhog.example>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink@mailhog.example")
|
||||
m, err = ParseMAIL("FROM:<oink>")
|
||||
m, err = proto.ParseMAIL("FROM:<oink>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink")
|
||||
})
|
||||
Convey("ParseMAIL should return an error for invalid syntax", t, func() {
|
||||
m, err := ParseMAIL("FROM:oink")
|
||||
m, err := proto.ParseMAIL("FROM:oink")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err.Error(), ShouldEqual, "Invalid syntax in MAIL command")
|
||||
So(m, ShouldEqual, "")
|
||||
})
|
||||
Convey("ParseMAIL should be case-insensitive", t, func() {
|
||||
m, err := ParseMAIL("FROM:<oink>")
|
||||
m, err := proto.ParseMAIL("FROM:<oink>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink")
|
||||
m, err = ParseMAIL("from:<oink@mailhog.example>")
|
||||
m, err = proto.ParseMAIL("from:<oink@mailhog.example>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink@mailhog.example")
|
||||
m, err = ParseMAIL("FrOm:<oink@oink.mailhog.example>")
|
||||
m, err = proto.ParseMAIL("FrOm:<oink@oink.mailhog.example>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink@oink.mailhog.example")
|
||||
})
|
||||
Convey("ParseMAIL should support broken sender syntax", t, func() {
|
||||
m, err := proto.ParseMAIL("FROM: <oink>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink")
|
||||
m, err = proto.ParseMAIL("from: <oink@mailhog.example>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink@mailhog.example")
|
||||
m, err = proto.ParseMAIL("FrOm: <oink@oink.mailhog.example>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink@oink.mailhog.example")
|
||||
})
|
||||
|
@ -586,28 +598,40 @@ func TestParseMAIL(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestParseRCPT(t *testing.T) {
|
||||
proto := NewProtocol()
|
||||
Convey("ParseRCPT should parse RCPT command arguments", t, func() {
|
||||
m, err := ParseRCPT("TO:<oink@mailhog.example>")
|
||||
m, err := proto.ParseRCPT("TO:<oink@mailhog.example>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink@mailhog.example")
|
||||
m, err = ParseRCPT("TO:<oink>")
|
||||
m, err = proto.ParseRCPT("TO:<oink>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink")
|
||||
})
|
||||
Convey("ParseRCPT should return an error for invalid syntax", t, func() {
|
||||
m, err := ParseRCPT("TO:oink")
|
||||
m, err := proto.ParseRCPT("TO:oink")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err.Error(), ShouldEqual, "Invalid syntax in RCPT command")
|
||||
So(m, ShouldEqual, "")
|
||||
})
|
||||
Convey("ParseRCPT should be case-insensitive", t, func() {
|
||||
m, err := ParseRCPT("TO:<oink>")
|
||||
m, err := proto.ParseRCPT("TO:<oink>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink")
|
||||
m, err = ParseRCPT("to:<oink@mailhog.example>")
|
||||
m, err = proto.ParseRCPT("to:<oink@mailhog.example>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink@mailhog.example")
|
||||
m, err = ParseRCPT("To:<oink@oink.mailhog.example>")
|
||||
m, err = proto.ParseRCPT("To:<oink@oink.mailhog.example>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink@oink.mailhog.example")
|
||||
})
|
||||
Convey("ParseRCPT should support broken recipient syntax", t, func() {
|
||||
m, err := proto.ParseRCPT("TO: <oink>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink")
|
||||
m, err = proto.ParseRCPT("to: <oink@mailhog.example>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink@mailhog.example")
|
||||
m, err = proto.ParseRCPT("To: <oink@oink.mailhog.example>")
|
||||
So(err, ShouldBeNil)
|
||||
So(m, ShouldEqual, "oink@oink.mailhog.example")
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue