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 }