Initial commit
This commit is contained in:
commit
ccc5b0cd98
26 changed files with 1806 additions and 0 deletions
86
checker/starttls_smtp.go
Normal file
86
checker/starttls_smtp.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package checker
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerStartTLS("smtp", starttlsSMTP)
|
||||
registerStartTLS("submission", starttlsSMTP)
|
||||
}
|
||||
|
||||
// starttlsSMTP implements ESMTP EHLO + STARTTLS (RFC 3207).
|
||||
func starttlsSMTP(conn net.Conn, sni string) error {
|
||||
rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
|
||||
|
||||
if err := readSMTPGreeting(rw.Reader); err != nil {
|
||||
return fmt.Errorf("read greeting: %w", err)
|
||||
}
|
||||
|
||||
if _, err := rw.WriteString("EHLO checker.happydomain.org\r\n"); err != nil {
|
||||
return fmt.Errorf("write ehlo: %w", err)
|
||||
}
|
||||
if err := rw.Flush(); err != nil {
|
||||
return fmt.Errorf("flush ehlo: %w", err)
|
||||
}
|
||||
lines, err := readSMTPResponse(rw.Reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read ehlo: %w", err)
|
||||
}
|
||||
if !hasSTARTTLSExt(lines) {
|
||||
return fmt.Errorf("%w: EHLO did not advertise STARTTLS", errStartTLSNotOffered)
|
||||
}
|
||||
|
||||
if _, err := rw.WriteString("STARTTLS\r\n"); err != nil {
|
||||
return fmt.Errorf("write starttls: %w", err)
|
||||
}
|
||||
if err := rw.Flush(); err != nil {
|
||||
return fmt.Errorf("flush starttls: %w", err)
|
||||
}
|
||||
resp, err := readSMTPResponse(rw.Reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read starttls: %w", err)
|
||||
}
|
||||
if len(resp) == 0 || !strings.HasPrefix(resp[0], "220") {
|
||||
return fmt.Errorf("server refused STARTTLS: %s", strings.Join(resp, " / "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readSMTPGreeting(r *bufio.Reader) error {
|
||||
_, err := readSMTPResponse(r)
|
||||
return err
|
||||
}
|
||||
|
||||
// readSMTPResponse reads one multi-line SMTP response (lines with "NNN-" are
|
||||
// continuation, "NNN " terminates).
|
||||
func readSMTPResponse(r *bufio.Reader) ([]string, error) {
|
||||
var out []string
|
||||
for {
|
||||
line, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
line = strings.TrimRight(line, "\r\n")
|
||||
out = append(out, line)
|
||||
if len(line) < 4 || line[3] == ' ' {
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func hasSTARTTLSExt(lines []string) bool {
|
||||
for _, l := range lines {
|
||||
if len(l) < 4 {
|
||||
continue
|
||||
}
|
||||
rest := strings.ToUpper(strings.TrimSpace(l[4:]))
|
||||
if rest == "STARTTLS" || strings.HasPrefix(rest, "STARTTLS ") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue