Bound line reads with readLineLimited to prevent a peer from exhausting memory by withholding line terminators, wrap previously bare error returns for consistent context, surface XML decoder Skip errors, and replace the goto in the XMPP feature scan with a labeled break. New starttls_test.go exercises SMTP/IMAP/POP3/XMPP/LDAP success and not-advertised paths through net.Pipe-mocked servers.
70 lines
1.6 KiB
Go
70 lines
1.6 KiB
Go
package checker
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
)
|
|
|
|
func init() {
|
|
registerStartTLS("pop3", starttlsPOP3)
|
|
}
|
|
|
|
// starttlsPOP3 implements RFC 2595 STLS.
|
|
func starttlsPOP3(conn net.Conn, sni string) error {
|
|
rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
|
|
|
|
greeting, err := readLineLimited(rw.Reader)
|
|
if err != nil {
|
|
return fmt.Errorf("read greeting: %w", err)
|
|
}
|
|
if !strings.HasPrefix(greeting, "+OK") {
|
|
return fmt.Errorf("unexpected POP3 greeting: %s", strings.TrimSpace(greeting))
|
|
}
|
|
|
|
if _, err := rw.WriteString("CAPA\r\n"); err != nil {
|
|
return fmt.Errorf("write CAPA: %w", err)
|
|
}
|
|
if err := rw.Flush(); err != nil {
|
|
return fmt.Errorf("flush CAPA: %w", err)
|
|
}
|
|
first, err := readLineLimited(rw.Reader)
|
|
if err != nil {
|
|
return fmt.Errorf("read CAPA: %w", err)
|
|
}
|
|
supportsSTLS := false
|
|
if strings.HasPrefix(first, "+OK") {
|
|
for {
|
|
line, err := readLineLimited(rw.Reader)
|
|
if err != nil {
|
|
return fmt.Errorf("read CAPA body: %w", err)
|
|
}
|
|
line = strings.TrimRight(line, "\r\n")
|
|
if line == "." {
|
|
break
|
|
}
|
|
if strings.EqualFold(line, "STLS") {
|
|
supportsSTLS = true
|
|
}
|
|
}
|
|
}
|
|
if !supportsSTLS {
|
|
return fmt.Errorf("%w: POP3 CAPA did not advertise STLS", errStartTLSNotOffered)
|
|
}
|
|
|
|
if _, err := rw.WriteString("STLS\r\n"); err != nil {
|
|
return fmt.Errorf("write STLS: %w", err)
|
|
}
|
|
if err := rw.Flush(); err != nil {
|
|
return fmt.Errorf("flush STLS: %w", err)
|
|
}
|
|
resp, err := readLineLimited(rw.Reader)
|
|
if err != nil {
|
|
return fmt.Errorf("read STLS response: %w", err)
|
|
}
|
|
if !strings.HasPrefix(resp, "+OK") {
|
|
return fmt.Errorf("server refused STLS: %s", strings.TrimSpace(resp))
|
|
}
|
|
return nil
|
|
}
|