mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 17:47:03 +00:00
This reverts commit 261bbef426. It was merged to directly master by mistake, and should have gone through the normal release process.
This commit is contained in:
@@ -29,25 +29,12 @@ const (
|
|||||||
GREET State = iota
|
GREET State = iota
|
||||||
// READY State: Got HELO, waiting for MAIL
|
// READY State: Got HELO, waiting for MAIL
|
||||||
READY
|
READY
|
||||||
// LOGIN State: Got AUTH LOGIN command, expecting Username
|
|
||||||
LOGIN
|
|
||||||
// PASSWORD State: Got Username, expecting password
|
|
||||||
PASSWORD
|
|
||||||
// MAIL State: Got MAIL, accepting RCPTs
|
// MAIL State: Got MAIL, accepting RCPTs
|
||||||
MAIL
|
MAIL
|
||||||
// DATA State: Got DATA, waiting for "."
|
// DATA State: Got DATA, waiting for "."
|
||||||
DATA
|
DATA
|
||||||
// QUIT State: Client requested end of session
|
// QUIT State: Client requested end of session
|
||||||
QUIT
|
QUIT
|
||||||
|
|
||||||
// Messages sent to user during LOGIN auth procedure
|
|
||||||
// Can vary, but values are taken directly from spec
|
|
||||||
// https://tools.ietf.org/html/draft-murchison-sasl-login-00
|
|
||||||
|
|
||||||
//usernameChallenge sent when inviting user to provide username. Is base64 encoded string `User Name`
|
|
||||||
usernameChallenge = "VXNlciBOYW1lAA=="
|
|
||||||
//passwordChallenge sent when inviting user to provide password. Is base64 encoded string `Password`
|
|
||||||
passwordChallenge = "UGFzc3dvcmQA"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// fromRegex captures the from address and optional BODY=8BITMIME clause. Matches FROM, while
|
// fromRegex captures the from address and optional BODY=8BITMIME clause. Matches FROM, while
|
||||||
@@ -89,7 +76,6 @@ var commands = map[string]bool{
|
|||||||
"QUIT": true,
|
"QUIT": true,
|
||||||
"TURN": true,
|
"TURN": true,
|
||||||
"STARTTLS": true,
|
"STARTTLS": true,
|
||||||
"AUTH": true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session holds the state of an SMTP session
|
// Session holds the state of an SMTP session
|
||||||
@@ -167,16 +153,6 @@ func (s *Server) startSession(id int, conn net.Conn) {
|
|||||||
}
|
}
|
||||||
line, err := ssn.readLine()
|
line, err := ssn.readLine()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
//Handle LOGIN/PASSWORD states here, because they don't expect a command
|
|
||||||
switch ssn.state {
|
|
||||||
case LOGIN:
|
|
||||||
ssn.loginHandler(line)
|
|
||||||
continue
|
|
||||||
case PASSWORD:
|
|
||||||
ssn.passwordHandler(line)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmd, arg, ok := ssn.parseCmd(line); ok {
|
if cmd, arg, ok := ssn.parseCmd(line); ok {
|
||||||
// Check against valid SMTP commands
|
// Check against valid SMTP commands
|
||||||
if cmd == "" {
|
if cmd == "" {
|
||||||
@@ -284,7 +260,6 @@ func (s *Session) greetHandler(cmd string, arg string) {
|
|||||||
// features before SIZE per RFC
|
// features before SIZE per RFC
|
||||||
s.send("250-" + readyBanner)
|
s.send("250-" + readyBanner)
|
||||||
s.send("250-8BITMIME")
|
s.send("250-8BITMIME")
|
||||||
s.send("250-AUTH PLAIN LOGIN")
|
|
||||||
if s.Server.config.TLSEnabled && s.Server.tlsConfig != nil && s.tlsState == nil {
|
if s.Server.config.TLSEnabled && s.Server.tlsConfig != nil && s.tlsState == nil {
|
||||||
s.send("250-STARTTLS")
|
s.send("250-STARTTLS")
|
||||||
}
|
}
|
||||||
@@ -306,28 +281,7 @@ func parseHelloArgument(arg string) (string, error) {
|
|||||||
return domain, nil
|
return domain, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) loginHandler(line string) {
|
|
||||||
if len(line) == 0 {
|
|
||||||
s.send("500 invalid Username")
|
|
||||||
s.enterState(READY)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.send(fmt.Sprintf("334 %v", passwordChallenge))
|
|
||||||
s.enterState(PASSWORD)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Session) passwordHandler(line string) {
|
|
||||||
if len(line) == 0 {
|
|
||||||
s.send("500 invalid Password")
|
|
||||||
s.enterState(READY)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.send("235 Authentication successful")
|
|
||||||
s.enterState(READY)
|
|
||||||
}
|
|
||||||
|
|
||||||
// READY state -> waiting for MAIL
|
// READY state -> waiting for MAIL
|
||||||
// AUTH can change
|
|
||||||
func (s *Session) readyHandler(cmd string, arg string) {
|
func (s *Session) readyHandler(cmd string, arg string) {
|
||||||
if cmd == "STARTTLS" {
|
if cmd == "STARTTLS" {
|
||||||
if !s.Server.config.TLSEnabled {
|
if !s.Server.config.TLSEnabled {
|
||||||
@@ -351,33 +305,6 @@ func (s *Session) readyHandler(cmd string, arg string) {
|
|||||||
s.tlsState = new(tls.ConnectionState)
|
s.tlsState = new(tls.ConnectionState)
|
||||||
*s.tlsState = tlsConn.ConnectionState()
|
*s.tlsState = tlsConn.ConnectionState()
|
||||||
s.enterState(GREET)
|
s.enterState(GREET)
|
||||||
} else if cmd == "AUTH" {
|
|
||||||
args := strings.SplitN(arg, " ", 3)
|
|
||||||
authMethod := args[0]
|
|
||||||
switch authMethod {
|
|
||||||
case "PLAIN":
|
|
||||||
{
|
|
||||||
if len(args) != 2 {
|
|
||||||
s.send("500 Bad auth arguments")
|
|
||||||
s.logger.Warn().Msgf("Bad auth attempt: %q", arg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.logger.Info().Msgf("Accepting credentials: %q", args[1])
|
|
||||||
s.send("235 2.7.0 Authentication successful")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case "LOGIN":
|
|
||||||
{
|
|
||||||
s.send(fmt.Sprintf("334 %v", usernameChallenge))
|
|
||||||
s.enterState(LOGIN)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
s.send(fmt.Sprintf("500 Unsupported AUTH method: %v", authMethod))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if cmd == "MAIL" {
|
} else if cmd == "MAIL" {
|
||||||
// Capture group 1: from address. 2: optional params.
|
// Capture group 1: from address. 2: optional params.
|
||||||
m := fromRegex.FindStringSubmatch(arg)
|
m := fromRegex.FindStringSubmatch(arg)
|
||||||
|
|||||||
@@ -108,46 +108,6 @@ func TestEmptyEnvelope(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test AUTH
|
|
||||||
func TestAuth(t *testing.T) {
|
|
||||||
ds := test.NewStore()
|
|
||||||
server, logbuf, teardown := setupSMTPServer(ds)
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
//PLAIN AUTH
|
|
||||||
script := []scriptStep{
|
|
||||||
{"EHLO localhost", 250},
|
|
||||||
{"AUTH PLAIN aW5idWNrZXQ6cGFzc3dvcmQK", 235},
|
|
||||||
{"RSET", 250},
|
|
||||||
{"AUTH GSSAPI aW5idWNrZXQ6cGFzc3dvcmQK", 500},
|
|
||||||
{"RSET", 250},
|
|
||||||
{"AUTH PLAIN", 500},
|
|
||||||
{"RSET", 250},
|
|
||||||
{"AUTH PLAIN aW5idWNrZXQ6cG Fzc3dvcmQK", 500},
|
|
||||||
}
|
|
||||||
if err := playSession(t, server, script); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//LOGIN AUTH
|
|
||||||
script = []scriptStep{
|
|
||||||
{"EHLO localhost", 250},
|
|
||||||
{"AUTH LOGIN", 334},
|
|
||||||
{"USERNAME", 334},
|
|
||||||
{"PASSWORD", 235},
|
|
||||||
}
|
|
||||||
if err := playSession(t, server, script); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Failed() {
|
|
||||||
// Wait for handler to finish logging
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
// Dump buffered log data if there was a failure
|
|
||||||
_, _ = io.Copy(os.Stderr, logbuf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test commands in READY state
|
// Test commands in READY state
|
||||||
func TestReadyState(t *testing.T) {
|
func TestReadyState(t *testing.T) {
|
||||||
ds := test.NewStore()
|
ds := test.NewStore()
|
||||||
|
|||||||
Reference in New Issue
Block a user