mirror of
https://gitlab.com/ric_harvey/MailHog.git
synced 2025-02-17 08:15:55 +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
|
// parameters are valid, otherwise false. If nil, all authentication
|
||||||
// attempts will be accepted.
|
// attempts will be accepted.
|
||||||
ValidateAuthenticationHandler func(mechanism string, args ...string) (errorReply *Reply, ok bool)
|
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
|
// NewProtocol returns a new SMTP state machine in INVALID state
|
||||||
|
@ -256,7 +265,7 @@ func (proto *Protocol) Command(command *Command) (reply *Reply) {
|
||||||
}
|
}
|
||||||
case "MAIL":
|
case "MAIL":
|
||||||
proto.logf("Got MAIL command, switching to RCPT state")
|
proto.logf("Got MAIL command, switching to RCPT state")
|
||||||
from, err := ParseMAIL(command.args)
|
from, err := proto.ParseMAIL(command.args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ReplyError(err)
|
return ReplyError(err)
|
||||||
}
|
}
|
||||||
|
@ -281,7 +290,7 @@ func (proto *Protocol) Command(command *Command) (reply *Reply) {
|
||||||
switch command.verb {
|
switch command.verb {
|
||||||
case "RCPT":
|
case "RCPT":
|
||||||
proto.logf("Got RCPT command")
|
proto.logf("Got RCPT command")
|
||||||
to, err := ParseRCPT(command.args)
|
to, err := proto.ParseRCPT(command.args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ReplyError(err)
|
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
|
// ParseMAIL returns the forward-path from a MAIL command argument
|
||||||
func ParseMAIL(mail string) (string, error) {
|
func (proto *Protocol) ParseMAIL(mail string) (string, error) {
|
||||||
r := regexp.MustCompile("(?i:From):<([^>]+)>")
|
var r *regexp.Regexp
|
||||||
|
if proto.RejectBrokenMAILSyntax {
|
||||||
|
r = regexp.MustCompile("(?i:From):<([^>]+)>")
|
||||||
|
} else {
|
||||||
|
r = regexp.MustCompile("(?i:From):\\s*<([^>]+)>")
|
||||||
|
}
|
||||||
match := r.FindStringSubmatch(mail)
|
match := r.FindStringSubmatch(mail)
|
||||||
if len(match) != 2 {
|
if len(match) != 2 {
|
||||||
return "", errors.New("Invalid syntax in MAIL command")
|
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
|
// ParseRCPT returns the return-path from a RCPT command argument
|
||||||
func ParseRCPT(rcpt string) (string, error) {
|
func (proto *Protocol) ParseRCPT(rcpt string) (string, error) {
|
||||||
r := regexp.MustCompile("(?i:To):<([^>]+)>")
|
var r *regexp.Regexp
|
||||||
|
if proto.RejectBrokenRCPTSyntax {
|
||||||
|
r = regexp.MustCompile("(?i:To):<([^>]+)>")
|
||||||
|
} else {
|
||||||
|
r = regexp.MustCompile("(?i:To):\\s*<([^>]+)>")
|
||||||
|
}
|
||||||
match := r.FindStringSubmatch(rcpt)
|
match := r.FindStringSubmatch(rcpt)
|
||||||
if len(match) != 2 {
|
if len(match) != 2 {
|
||||||
return "", errors.New("Invalid syntax in RCPT command")
|
return "", errors.New("Invalid syntax in RCPT command")
|
||||||
|
|
|
@ -511,28 +511,40 @@ func TestQUIT(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMAIL(t *testing.T) {
|
func TestParseMAIL(t *testing.T) {
|
||||||
|
proto := NewProtocol()
|
||||||
Convey("ParseMAIL should parse MAIL command arguments", t, func() {
|
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(err, ShouldBeNil)
|
||||||
So(m, ShouldEqual, "oink@mailhog.example")
|
So(m, ShouldEqual, "oink@mailhog.example")
|
||||||
m, err = ParseMAIL("FROM:<oink>")
|
m, err = proto.ParseMAIL("FROM:<oink>")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(m, ShouldEqual, "oink")
|
So(m, ShouldEqual, "oink")
|
||||||
})
|
})
|
||||||
Convey("ParseMAIL should return an error for invalid syntax", t, func() {
|
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, ShouldNotBeNil)
|
||||||
So(err.Error(), ShouldEqual, "Invalid syntax in MAIL command")
|
So(err.Error(), ShouldEqual, "Invalid syntax in MAIL command")
|
||||||
So(m, ShouldEqual, "")
|
So(m, ShouldEqual, "")
|
||||||
})
|
})
|
||||||
Convey("ParseMAIL should be case-insensitive", t, func() {
|
Convey("ParseMAIL should be case-insensitive", t, func() {
|
||||||
m, err := ParseMAIL("FROM:<oink>")
|
m, err := proto.ParseMAIL("FROM:<oink>")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(m, ShouldEqual, "oink")
|
So(m, ShouldEqual, "oink")
|
||||||
m, err = ParseMAIL("from:<oink@mailhog.example>")
|
m, err = proto.ParseMAIL("from:<oink@mailhog.example>")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(m, ShouldEqual, "oink@mailhog.example")
|
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(err, ShouldBeNil)
|
||||||
So(m, ShouldEqual, "oink@oink.mailhog.example")
|
So(m, ShouldEqual, "oink@oink.mailhog.example")
|
||||||
})
|
})
|
||||||
|
@ -586,28 +598,40 @@ func TestParseMAIL(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseRCPT(t *testing.T) {
|
func TestParseRCPT(t *testing.T) {
|
||||||
|
proto := NewProtocol()
|
||||||
Convey("ParseRCPT should parse RCPT command arguments", t, func() {
|
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(err, ShouldBeNil)
|
||||||
So(m, ShouldEqual, "oink@mailhog.example")
|
So(m, ShouldEqual, "oink@mailhog.example")
|
||||||
m, err = ParseRCPT("TO:<oink>")
|
m, err = proto.ParseRCPT("TO:<oink>")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(m, ShouldEqual, "oink")
|
So(m, ShouldEqual, "oink")
|
||||||
})
|
})
|
||||||
Convey("ParseRCPT should return an error for invalid syntax", t, func() {
|
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, ShouldNotBeNil)
|
||||||
So(err.Error(), ShouldEqual, "Invalid syntax in RCPT command")
|
So(err.Error(), ShouldEqual, "Invalid syntax in RCPT command")
|
||||||
So(m, ShouldEqual, "")
|
So(m, ShouldEqual, "")
|
||||||
})
|
})
|
||||||
Convey("ParseRCPT should be case-insensitive", t, func() {
|
Convey("ParseRCPT should be case-insensitive", t, func() {
|
||||||
m, err := ParseRCPT("TO:<oink>")
|
m, err := proto.ParseRCPT("TO:<oink>")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(m, ShouldEqual, "oink")
|
So(m, ShouldEqual, "oink")
|
||||||
m, err = ParseRCPT("to:<oink@mailhog.example>")
|
m, err = proto.ParseRCPT("to:<oink@mailhog.example>")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(m, ShouldEqual, "oink@mailhog.example")
|
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(err, ShouldBeNil)
|
||||||
So(m, ShouldEqual, "oink@oink.mailhog.example")
|
So(m, ShouldEqual, "oink@oink.mailhog.example")
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Reference in a new issue