1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2025-12-17 14:37:02 +00:00

smtpsrv: Send enhanced status codes

SMTP supports enhanced status codes, which help with
internationalization and accessibility in cases where protocol errors
make their way to the users.

This patch makes chasquid include these extended status codes in the
corresponding replies, as well as advertising support in the EHLO reply.

Main references:
- RFC 3463 (https://tools.ietf.org/html/rfc3463)
- RFC 2034 (https://tools.ietf.org/html/rfc2034)
- SMTP Enhanced Status Codes Registry
  (https://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes.xhtml)
This commit is contained in:
Alberto Bertogli
2019-01-09 23:43:11 +00:00
parent 78937aca93
commit e7309a2c7b
9 changed files with 78 additions and 81 deletions

View File

@@ -232,14 +232,14 @@ loop:
case "AUTH": case "AUTH":
code, msg = c.AUTH(params) code, msg = c.AUTH(params)
case "QUIT": case "QUIT":
c.writeResponse(221, "Be seeing you...") c.writeResponse(221, "2.0.0 Be seeing you...")
break loop break loop
default: default:
// Sanitize it a bit to avoid filling the logs and events with // Sanitize it a bit to avoid filling the logs and events with
// noisy data. Keep the first 6 bytes for debugging. // noisy data. Keep the first 6 bytes for debugging.
cmd = fmt.Sprintf("unknown<%.6s>", cmd) cmd = fmt.Sprintf("unknown<%.6s>", cmd)
code = 500 code = 500
msg = "unknown command" msg = "5.5.1 Unknown command"
} }
commandCount.Add(cmd, 1) commandCount.Add(cmd, 1)
@@ -254,7 +254,7 @@ loop:
if errCount > 10 { if errCount > 10 {
// https://tools.ietf.org/html/rfc5321#section-4.3.2 // https://tools.ietf.org/html/rfc5321#section-4.3.2
c.tr.Errorf("too many errors, breaking connection") c.tr.Errorf("too many errors, breaking connection")
c.writeResponse(421, "too many errors, bye") c.writeResponse(421, "4.5.0 Too many errors, bye")
break break
} }
} }
@@ -302,6 +302,7 @@ func (c *Conn) EHLO(params string) (code int, msg string) {
fmt.Fprintf(buf, "8BITMIME\n") fmt.Fprintf(buf, "8BITMIME\n")
fmt.Fprintf(buf, "PIPELINING\n") fmt.Fprintf(buf, "PIPELINING\n")
fmt.Fprintf(buf, "SMTPUTF8\n") fmt.Fprintf(buf, "SMTPUTF8\n")
fmt.Fprintf(buf, "ENHANCEDSTATUSCODES\n")
fmt.Fprintf(buf, "SIZE %d\n", c.maxDataSize) fmt.Fprintf(buf, "SIZE %d\n", c.maxDataSize)
if c.onTLS { if c.onTLS {
fmt.Fprintf(buf, "AUTH PLAIN\n") fmt.Fprintf(buf, "AUTH PLAIN\n")
@@ -314,7 +315,7 @@ func (c *Conn) EHLO(params string) (code int, msg string) {
// HELP SMTP command handler. // HELP SMTP command handler.
func (c *Conn) HELP(params string) (code int, msg string) { func (c *Conn) HELP(params string) (code int, msg string) {
return 214, "hoy por ti, mañana por mi" return 214, "2.0.0 Hoy por ti, mañana por mi"
} }
// RSET SMTP command handler. // RSET SMTP command handler.
@@ -327,28 +328,24 @@ func (c *Conn) RSET(params string) (code int, msg string) {
"Your mind releases itself from mundane concerns.", "Your mind releases itself from mundane concerns.",
"As your mind turns inward on itself, you forget everything else.", "As your mind turns inward on itself, you forget everything else.",
} }
return 250, msgs[rand.Int()%len(msgs)] return 250, "2.0.0 " + msgs[rand.Int()%len(msgs)]
} }
// VRFY SMTP command handler. // VRFY SMTP command handler.
func (c *Conn) VRFY(params string) (code int, msg string) { func (c *Conn) VRFY(params string) (code int, msg string) {
// 252 can be used for cases like ours, when we don't really want to // We intentionally don't implement this command.
// confirm or deny anything. return 502, "5.5.1 You have a strange feeling for a moment, then it passes."
// See https://tools.ietf.org/html/rfc2821#section-3.5.3.
return 252, "You have a strange feeling for a moment, then it passes."
} }
// EXPN SMTP command handler. // EXPN SMTP command handler.
func (c *Conn) EXPN(params string) (code int, msg string) { func (c *Conn) EXPN(params string) (code int, msg string) {
// 252 can be used for cases like ours, when we don't really want to // We intentionally don't implement this command.
// confirm or deny anything. return 502, "5.5.1 You feel disoriented for a moment."
// See https://tools.ietf.org/html/rfc2821#section-3.5.3.
return 252, "You feel disoriented for a moment."
} }
// NOOP SMTP command handler. // NOOP SMTP command handler.
func (c *Conn) NOOP(params string) (code int, msg string) { func (c *Conn) NOOP(params string) (code int, msg string) {
return 250, "You hear a faint typing noise." return 250, "2.0.0 You hear a faint typing noise."
} }
// MAIL SMTP command handler. // MAIL SMTP command handler.
@@ -357,16 +354,16 @@ func (c *Conn) MAIL(params string) (code int, msg string) {
// options such as "BODY=8BITMIME" (which we ignore). // options such as "BODY=8BITMIME" (which we ignore).
// Check that it begins with "FROM:" first, it's mandatory. // Check that it begins with "FROM:" first, it's mandatory.
if !strings.HasPrefix(strings.ToLower(params), "from:") { if !strings.HasPrefix(strings.ToLower(params), "from:") {
return 500, "unknown command" return 500, "5.5.2 Unknown command"
} }
if c.mode.IsSubmission && !c.completedAuth { if c.mode.IsSubmission && !c.completedAuth {
return 550, "mail to submission port must be authenticated" return 550, "5.7.9 Mail to submission port must be authenticated"
} }
rawAddr := "" rawAddr := ""
_, err := fmt.Sscanf(params[5:], "%s ", &rawAddr) _, err := fmt.Sscanf(params[5:], "%s ", &rawAddr)
if err != nil { if err != nil {
return 500, "malformed command - " + err.Error() return 500, "5.5.4 Malformed command: " + err.Error()
} }
// Note some servers check (and fail) if we had a previous MAIL command, // Note some servers check (and fail) if we had a previous MAIL command,
@@ -383,17 +380,17 @@ func (c *Conn) MAIL(params string) (code int, msg string) {
} else { } else {
e, err := mail.ParseAddress(rawAddr) e, err := mail.ParseAddress(rawAddr)
if err != nil || e.Address == "" { if err != nil || e.Address == "" {
return 501, "malformed address" return 501, "5.1.7 Sender address malformed"
} }
addr = e.Address addr = e.Address
if !strings.Contains(addr, "@") { if !strings.Contains(addr, "@") {
return 501, "sender address must contain a domain" return 501, "5.1.8 Sender address must contain a domain"
} }
// https://tools.ietf.org/html/rfc5321#section-4.5.3.1.3 // https://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
if len(addr) > 256 { if len(addr) > 256 {
return 501, "address too long" return 501, "5.1.7 Sender address too long"
} }
// SPF check - https://tools.ietf.org/html/rfc7208#section-2.4 // SPF check - https://tools.ietf.org/html/rfc7208#section-2.4
@@ -405,25 +402,25 @@ func (c *Conn) MAIL(params string) (code int, msg string) {
maillog.Rejected(c.conn.RemoteAddr(), addr, nil, maillog.Rejected(c.conn.RemoteAddr(), addr, nil,
fmt.Sprintf("failed SPF: %v", c.spfError)) fmt.Sprintf("failed SPF: %v", c.spfError))
return 550, fmt.Sprintf( return 550, fmt.Sprintf(
"SPF check failed: %v", c.spfError) "5.7.23 SPF check failed: %v", c.spfError)
} }
if !c.secLevelCheck(addr) { if !c.secLevelCheck(addr) {
maillog.Rejected(c.conn.RemoteAddr(), addr, nil, maillog.Rejected(c.conn.RemoteAddr(), addr, nil,
"security level check failed") "security level check failed")
return 550, "security level check failed" return 550, "5.7.3 Security level check failed"
} }
addr, err = normalize.DomainToUnicode(addr) addr, err = normalize.DomainToUnicode(addr)
if err != nil { if err != nil {
maillog.Rejected(c.conn.RemoteAddr(), addr, nil, maillog.Rejected(c.conn.RemoteAddr(), addr, nil,
fmt.Sprintf("malformed address: %v", err)) fmt.Sprintf("malformed address: %v", err))
return 501, "malformed address (IDNA conversion failed)" return 501, "5.1.8 Malformed sender domain (IDNA conversion failed)"
} }
} }
c.mailFrom = addr c.mailFrom = addr
return 250, "You feel like you are being watched" return 250, "2.1.5 You feel like you are being watched"
} }
// checkSPF for the given address, based on the current connection. // checkSPF for the given address, based on the current connection.
@@ -487,45 +484,45 @@ func (c *Conn) RCPT(params string) (code int, msg string) {
// such as "NOTIFY=SUCCESS,DELAY" (which we ignore). // such as "NOTIFY=SUCCESS,DELAY" (which we ignore).
// Check that it begins with "TO:" first, it's mandatory. // Check that it begins with "TO:" first, it's mandatory.
if !strings.HasPrefix(strings.ToLower(params), "to:") { if !strings.HasPrefix(strings.ToLower(params), "to:") {
return 500, "unknown command" return 500, "5.5.2 Unknown command"
} }
if c.mailFrom == "" { if c.mailFrom == "" {
return 503, "sender not yet given" return 503, "5.5.1 Sender not yet given"
} }
rawAddr := "" rawAddr := ""
_, err := fmt.Sscanf(params[3:], "%s ", &rawAddr) _, err := fmt.Sscanf(params[3:], "%s ", &rawAddr)
if err != nil { if err != nil {
return 500, "malformed command - " + err.Error() return 500, "5.5.4 Malformed command: " + err.Error()
} }
// RFC says 100 is the minimum limit for this, but it seems excessive. // RFC says 100 is the minimum limit for this, but it seems excessive.
// https://tools.ietf.org/html/rfc5321#section-4.5.3.1.8 // https://tools.ietf.org/html/rfc5321#section-4.5.3.1.8
if len(c.rcptTo) > 100 { if len(c.rcptTo) > 100 {
return 452, "too many recipients" return 452, "4.5.3 Too many recipients"
} }
e, err := mail.ParseAddress(rawAddr) e, err := mail.ParseAddress(rawAddr)
if err != nil || e.Address == "" { if err != nil || e.Address == "" {
return 501, "malformed address" return 501, "5.1.3 Malformed destination address"
} }
addr, err := normalize.DomainToUnicode(e.Address) addr, err := normalize.DomainToUnicode(e.Address)
if err != nil { if err != nil {
return 501, "malformed address (IDNA conversion failed)" return 501, "5.1.2 Malformed destination domain (IDNA conversion failed)"
} }
// https://tools.ietf.org/html/rfc5321#section-4.5.3.1.3 // https://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
if len(addr) > 256 { if len(addr) > 256 {
return 501, "address too long" return 501, "5.1.3 Destination address too long"
} }
localDst := envelope.DomainIn(addr, c.localDomains) localDst := envelope.DomainIn(addr, c.localDomains)
if !localDst && !c.completedAuth { if !localDst && !c.completedAuth {
maillog.Rejected(c.conn.RemoteAddr(), c.mailFrom, []string{addr}, maillog.Rejected(c.conn.RemoteAddr(), c.mailFrom, []string{addr},
"relay not allowed") "relay not allowed")
return 503, "relay not allowed" return 503, "5.7.1 Relay not allowed"
} }
if localDst { if localDst {
@@ -533,36 +530,36 @@ func (c *Conn) RCPT(params string) (code int, msg string) {
if err != nil { if err != nil {
maillog.Rejected(c.conn.RemoteAddr(), c.mailFrom, []string{addr}, maillog.Rejected(c.conn.RemoteAddr(), c.mailFrom, []string{addr},
fmt.Sprintf("invalid address: %v", err)) fmt.Sprintf("invalid address: %v", err))
return 550, "recipient invalid, please check the address for typos" return 550, "5.1.3 Destination address is invalid"
} }
if !c.userExists(addr) { if !c.userExists(addr) {
maillog.Rejected(c.conn.RemoteAddr(), c.mailFrom, []string{addr}, maillog.Rejected(c.conn.RemoteAddr(), c.mailFrom, []string{addr},
"local user does not exist") "local user does not exist")
return 550, "recipient unknown, please check the address for typos" return 550, "5.1.1 Destination address is unknown (user does not exist)"
} }
} }
c.rcptTo = append(c.rcptTo, addr) c.rcptTo = append(c.rcptTo, addr)
return 250, "You have an eerie feeling..." return 250, "2.1.5 You have an eerie feeling..."
} }
// DATA SMTP command handler. // DATA SMTP command handler.
func (c *Conn) DATA(params string) (code int, msg string) { func (c *Conn) DATA(params string) (code int, msg string) {
if c.ehloAddress == "" { if c.ehloAddress == "" {
return 503, "Invisible customers are not welcome!" return 503, "5.5.1 Invisible customers are not welcome!"
} }
if c.mailFrom == "" { if c.mailFrom == "" {
return 503, "sender not yet given" return 503, "5.5.1 Sender not yet given"
} }
if len(c.rcptTo) == 0 { if len(c.rcptTo) == 0 {
return 503, "need an address to send to" return 503, "5.5.1 Need an address to send to"
} }
// We're going ahead. // We're going ahead.
err := c.writeResponse(354, "You suddenly realize it is unnaturally quiet") err := c.writeResponse(354, "You suddenly realize it is unnaturally quiet")
if err != nil { if err != nil {
return 554, fmt.Sprintf("error writing DATA response: %v", err) return 554, fmt.Sprintf("5.4.0 Error writing DATA response: %v", err)
} }
c.tr.Debugf("<- 354 You experience a strange sense of peace") c.tr.Debugf("<- 354 You experience a strange sense of peace")
@@ -579,7 +576,7 @@ func (c *Conn) DATA(params string) (code int, msg string) {
dotr := io.LimitReader(c.tc.DotReader(), c.maxDataSize) dotr := io.LimitReader(c.tc.DotReader(), c.maxDataSize)
c.data, err = ioutil.ReadAll(dotr) c.data, err = ioutil.ReadAll(dotr)
if err != nil { if err != nil {
return 554, fmt.Sprintf("error reading DATA: %v", err) return 554, fmt.Sprintf("5.4.0 Error reading DATA: %v", err)
} }
c.tr.Debugf("-> ... %d bytes of data", len(c.data)) c.tr.Debugf("-> ... %d bytes of data", len(c.data))
@@ -605,7 +602,7 @@ func (c *Conn) DATA(params string) (code int, msg string) {
// individual deliveries fail, we report via email. // individual deliveries fail, we report via email.
msgID, err := c.queue.Put(c.mailFrom, c.rcptTo, c.data) msgID, err := c.queue.Put(c.mailFrom, c.rcptTo, c.data)
if err != nil { if err != nil {
return 554, fmt.Sprintf("Failed to enqueue message: %v", err) return 554, fmt.Sprintf("5.3.0 Failed to queue message: %v", err)
} }
c.tr.Printf("Queued from %s to %s - %s", c.mailFrom, c.rcptTo, msgID) c.tr.Printf("Queued from %s to %s - %s", c.mailFrom, c.rcptTo, msgID)
@@ -622,7 +619,7 @@ func (c *Conn) DATA(params string) (code int, msg string) {
"In return to thy service, I grant thee the gift of Immortality!", "In return to thy service, I grant thee the gift of Immortality!",
"You ascend to the status of Demigod(dess)...", "You ascend to the status of Demigod(dess)...",
} }
return 250, msgs[rand.Int()%len(msgs)] return 250, "2.0.0 " + msgs[rand.Int()%len(msgs)]
} }
func (c *Conn) addReceivedHeader() { func (c *Conn) addReceivedHeader() {
@@ -715,7 +712,7 @@ func addrLiteral(addr net.Addr) string {
func checkData(data []byte) error { func checkData(data []byte) error {
msg, err := mail.ReadMessage(bytes.NewBuffer(data)) msg, err := mail.ReadMessage(bytes.NewBuffer(data))
if err != nil { if err != nil {
return fmt.Errorf("error parsing message: %v", err) return fmt.Errorf("5.6.0 Error parsing message: %v", err)
} }
// This serves as a basic form of loop prevention. It's not infallible but // This serves as a basic form of loop prevention. It's not infallible but
@@ -723,7 +720,7 @@ func checkData(data []byte) error {
// https://tools.ietf.org/html/rfc5321#section-6.3 // https://tools.ietf.org/html/rfc5321#section-6.3
if len(msg.Header["Received"]) > *maxReceivedHeaders { if len(msg.Header["Received"]) > *maxReceivedHeaders {
loopsDetected.Add(1) loopsDetected.Add(1)
return fmt.Errorf("email passed through more than %d MTAs, looping?", return fmt.Errorf("5.4.6 Loop detected (%d hops)",
*maxReceivedHeaders) *maxReceivedHeaders)
} }
@@ -858,12 +855,12 @@ func boolToStr(b bool) string {
// STARTTLS SMTP command handler. // STARTTLS SMTP command handler.
func (c *Conn) STARTTLS(params string) (code int, msg string) { func (c *Conn) STARTTLS(params string) (code int, msg string) {
if c.onTLS { if c.onTLS {
return 503, "You are already wearing that!" return 503, "5.5.1 You are already wearing that!"
} }
err := c.writeResponse(220, "You experience a strange sense of peace") err := c.writeResponse(220, "2.0.0 You experience a strange sense of peace")
if err != nil { if err != nil {
return 554, fmt.Sprintf("error writing STARTTLS response: %v", err) return 554, fmt.Sprintf("5.4.0 Error writing STARTTLS response: %v", err)
} }
c.tr.Debugf("<- 220 You experience a strange sense of peace") c.tr.Debugf("<- 220 You experience a strange sense of peace")
@@ -871,7 +868,7 @@ func (c *Conn) STARTTLS(params string) (code int, msg string) {
server := tls.Server(c.conn, c.tlsConfig) server := tls.Server(c.conn, c.tlsConfig)
err = server.Handshake() err = server.Handshake()
if err != nil { if err != nil {
return 554, fmt.Sprintf("error in TLS handshake: %v", err) return 554, fmt.Sprintf("5.5.0 Error in TLS handshake: %v", err)
} }
c.tr.Debugf("<> ... jump to TLS was successful") c.tr.Debugf("<> ... jump to TLS was successful")
@@ -903,18 +900,18 @@ func (c *Conn) STARTTLS(params string) (code int, msg string) {
// AUTH SMTP command handler. // AUTH SMTP command handler.
func (c *Conn) AUTH(params string) (code int, msg string) { func (c *Conn) AUTH(params string) (code int, msg string) {
if !c.onTLS { if !c.onTLS {
return 503, "You feel vulnerable" return 503, "5.7.10 You feel vulnerable"
} }
if c.completedAuth { if c.completedAuth {
// After a successful AUTH command completes, a server MUST reject // After a successful AUTH command completes, a server MUST reject
// any further AUTH commands with a 503 reply. // any further AUTH commands with a 503 reply.
// https://tools.ietf.org/html/rfc4954#section-4 // https://tools.ietf.org/html/rfc4954#section-4
return 503, "You are already wearing that!" return 503, "5.5.1 You are already wearing that!"
} }
if c.authAttempts > 3 { if c.authAttempts > 3 {
return 503, "Too many attempts - go away" return 503, "5.7.8 Too many attempts, go away"
} }
c.authAttempts++ c.authAttempts++
@@ -926,7 +923,7 @@ func (c *Conn) AUTH(params string) (code int, msg string) {
sp := strings.SplitN(params, " ", 2) sp := strings.SplitN(params, " ", 2)
if len(sp) < 1 || sp[0] != "PLAIN" { if len(sp) < 1 || sp[0] != "PLAIN" {
// As we only offer plain, this should not really happen. // As we only offer plain, this should not really happen.
return 534, "Asmodeus demands 534 zorkmids for safe passage" return 534, "5.7.9 Asmodeus demands 534 zorkmids for safe passage"
} }
// Note we use more "serious" error messages from now own, as these may // Note we use more "serious" error messages from now own, as these may
@@ -943,18 +940,19 @@ func (c *Conn) AUTH(params string) (code int, msg string) {
// https://tools.ietf.org/html/rfc4954#section-4 // https://tools.ietf.org/html/rfc4954#section-4
err := c.writeResponse(334, "") err := c.writeResponse(334, "")
if err != nil { if err != nil {
return 554, fmt.Sprintf("error writing AUTH 334: %v", err) return 554, fmt.Sprintf("5.4.0 Error writing AUTH 334: %v", err)
} }
response, err = c.readLine() response, err = c.readLine()
if err != nil { if err != nil {
return 554, fmt.Sprintf("error reading AUTH response: %v", err) return 554, fmt.Sprintf("5.4.0 Error reading AUTH response: %v", err)
} }
} }
user, domain, passwd, err := auth.DecodeResponse(response) user, domain, passwd, err := auth.DecodeResponse(response)
if err != nil { if err != nil {
return 535, fmt.Sprintf("error decoding AUTH response: %v", err) // https://tools.ietf.org/html/rfc4954#section-4
return 501, fmt.Sprintf("5.5.2 Error decoding AUTH response: %v", err)
} }
authOk, err := c.authr.Authenticate(user, domain, passwd) authOk, err := c.authr.Authenticate(user, domain, passwd)
@@ -966,11 +964,11 @@ func (c *Conn) AUTH(params string) (code int, msg string) {
c.authDomain = domain c.authDomain = domain
c.completedAuth = true c.completedAuth = true
maillog.Auth(c.conn.RemoteAddr(), user+"@"+domain, true) maillog.Auth(c.conn.RemoteAddr(), user+"@"+domain, true)
return 235, "" return 235, "2.7.0 Authentication successful"
} }
maillog.Auth(c.conn.RemoteAddr(), user+"@"+domain, false) maillog.Auth(c.conn.RemoteAddr(), user+"@"+domain, false)
return 535, "Incorrect user or password" return 535, "5.7.8 Incorrect user or password"
} }
func (c *Conn) resetEnvelope() { func (c *Conn) resetEnvelope() {

View File

@@ -272,8 +272,8 @@ func TestSimpleCommands(t *testing.T) {
defer c.Close() defer c.Close()
simpleCmd(t, c, "HELP", 214) simpleCmd(t, c, "HELP", 214)
simpleCmd(t, c, "NOOP", 250) simpleCmd(t, c, "NOOP", 250)
simpleCmd(t, c, "VRFY", 252) simpleCmd(t, c, "VRFY", 502)
simpleCmd(t, c, "EXPN", 252) simpleCmd(t, c, "EXPN", 502)
} }
func TestReset(t *testing.T) { func TestReset(t *testing.T) {

View File

@@ -11,17 +11,17 @@ c <~ 534
c -> AUTH PLAIN c -> AUTH PLAIN
c <~ 334 c <~ 334
c -> dXNlckB0ZXN0c2VydmVyAHlalala== c -> dXNlckB0ZXN0c2VydmVyAHlalala==
c <~ 535 error decoding AUTH response c <~ 501 5.5.2 Error decoding AUTH response
c -> AUTH PLAIN c -> AUTH PLAIN
c <~ 334 c <~ 334
c -> dXNlckB0ZXN0c2VydmVyAHVzZXJAdGVzdHNlcnZlcgB3cm9uZ3Bhc3N3b3Jk c -> dXNlckB0ZXN0c2VydmVyAHVzZXJAdGVzdHNlcnZlcgB3cm9uZ3Bhc3N3b3Jk
c <~ 535 Incorrect user or password c <~ 535 5.7.8 Incorrect user or password
c -> AUTH PLAIN c -> AUTH PLAIN
c <~ 334 c <~ 334
c -> dXNlckB0ZXN0c2VydmVyAHVzZXJAdGVzdHNlcnZlcgBzZWNyZXRwYXNzd29yZA== c -> dXNlckB0ZXN0c2VydmVyAHVzZXJAdGVzdHNlcnZlcgBzZWNyZXRwYXNzd29yZA==
c <~ 235 c <~ 235 2.7.0 Authentication successful
c -> AUTH PLAIN c -> AUTH PLAIN
c <~ 503 You are already wearing that! c <~ 503 5.5.1 You are already wearing that!

View File

@@ -5,4 +5,4 @@ c <~ 220
c -> EHLO localhost c -> EHLO localhost
c <... 250 HELP c <... 250 HELP
c -> AUTH PLAIN c -> AUTH PLAIN
c <- 503 You feel vulnerable c <- 503 5.7.10 You feel vulnerable

View File

@@ -6,13 +6,13 @@ c -> EHLO localhost
c <... 250 HELP c <... 250 HELP
c -> AUTH PLAIN something c -> AUTH PLAIN something
c <~ 535 c <~ 501
c -> AUTH PLAIN something c -> AUTH PLAIN something
c <~ 535 c <~ 501
c -> AUTH PLAIN something c -> AUTH PLAIN something
c <~ 535 c <~ 501
c -> AUTH PLAIN something c -> AUTH PLAIN something
c <~ 535 c <~ 501
c -> AUTH PLAIN something c -> AUTH PLAIN something
c <~ 503 Too many attempts - go away c <~ 503 5.7.8 Too many attempts, go away

View File

@@ -4,12 +4,12 @@ c tcp_connect localhost:1025
c <~ 220 c <~ 220
c -> DATA c -> DATA
c <- 503 Invisible customers are not welcome! c <- 503 5.5.1 Invisible customers are not welcome!
c -> HELO localhost c -> HELO localhost
c <~ 250 c <~ 250
c -> DATA c -> DATA
c <- 503 sender not yet given c <- 503 5.5.1 Sender not yet given
c -> MAIL FROM:<a@b> c -> MAIL FROM:<a@b>
c <~ 250 c <~ 250
@@ -25,7 +25,7 @@ c -> Muahahahaha
c -> c ->
c -> c ->
c -> . c -> .
c <~ 554 error parsing message c <~ 554 5.6.0 Error parsing message
c -> QUIT c -> QUIT
c <~ 221 c <~ 221

View File

@@ -5,7 +5,7 @@ c <~ 220
c -> HELO localhost c -> HELO localhost
c <~ 250 c <~ 250
c -> MAIL LALA: <> c -> MAIL LALA: <>
c <- 500 unknown command c <- 500 5.5.2 Unknown command
c -> MAIL FROM: c -> MAIL FROM:
c <~ 500 c <~ 500
@@ -14,7 +14,7 @@ c -> MAIL FROM:<pepe>
c <~ 501 c <~ 501
c -> MAIL FROM:<a@xn---> c -> MAIL FROM:<a@xn--->
c <- 501 malformed address (IDNA conversion failed) c <- 501 5.1.8 Malformed sender domain (IDNA conversion failed)
c -> MAIL FROM:<aaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaX@bbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbX> c -> MAIL FROM:<aaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaX@bbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbX>
c <- 501 address too long c <- 501 5.1.7 Sender address too long

View File

@@ -8,7 +8,7 @@ c -> MAIL FROM:<test@testy.com>
c <~ 250 c <~ 250
c -> RCPT LALA: <> c -> RCPT LALA: <>
c <- 500 unknown command c <- 500 5.5.2 Unknown command
c -> RCPT TO: c -> RCPT TO:
c <~ 500 c <~ 500
@@ -17,10 +17,10 @@ c -> RCPT TO:<pepe>
c <~ 501 c <~ 501
c -> RCPT TO:<a@xn---> c -> RCPT TO:<a@xn--->
c <- 501 malformed address (IDNA conversion failed) c <- 501 5.1.2 Malformed destination domain (IDNA conversion failed)
c -> RCPT TO:<henryⅣ@testserver> c -> RCPT TO:<henryⅣ@testserver>
c <- 550 recipient invalid, please check the address for typos c <- 550 5.1.3 Destination address is invalid
c -> RCPT TO:<aaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaX@bbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbX> c -> RCPT TO:<aaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaXaaaa5aaaaX@bbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbXbbbb5bbbbX>
c <- 501 address too long c <- 501 5.1.3 Destination address too long

View File

@@ -5,5 +5,4 @@ c <~ 220
c -> EHLO localhost c -> EHLO localhost
c <... 250 HELP c <... 250 HELP
c -> WHATISTHIS c -> WHATISTHIS
c <- 500 unknown command c <- 500 5.5.1 Unknown command