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.
50 lines
1.6 KiB
Go
50 lines
1.6 KiB
Go
package checker
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
)
|
|
|
|
// maxSTARTTLSLineBytes caps the length of a single line read from a STARTTLS
|
|
// peer. Real banners and CAPABILITY responses are well under 1 KiB; this
|
|
// bound prevents a malicious or buggy server from exhausting memory by
|
|
// withholding the line terminator.
|
|
const maxSTARTTLSLineBytes = 8 * 1024
|
|
|
|
// readLineLimited reads bytes from r up to and including the next '\n', or
|
|
// until maxSTARTTLSLineBytes have been read without one (in which case it
|
|
// returns an error). The returned string keeps the trailing '\n' so callers
|
|
// can use the same parsing logic as bufio.Reader.ReadString('\n').
|
|
func readLineLimited(r *bufio.Reader) (string, error) {
|
|
out := make([]byte, 0, 128)
|
|
for {
|
|
b, err := r.ReadByte()
|
|
if err != nil {
|
|
if err == io.EOF && len(out) > 0 {
|
|
return string(out), io.ErrUnexpectedEOF
|
|
}
|
|
return string(out), err
|
|
}
|
|
out = append(out, b)
|
|
if b == '\n' {
|
|
return string(out), nil
|
|
}
|
|
if len(out) >= maxSTARTTLSLineBytes {
|
|
return string(out), fmt.Errorf("line exceeds %d bytes without terminator", maxSTARTTLSLineBytes)
|
|
}
|
|
}
|
|
}
|
|
|
|
// starttlsUpgrader performs the plaintext portion of a STARTTLS upgrade on
|
|
// conn, leaving conn ready for tls.Client(conn, …).Handshake(). On success
|
|
// the returned function returns nil; on failure it returns a descriptive
|
|
// error (wrap errStartTLSNotOffered when the server advertises no STARTTLS).
|
|
type starttlsUpgrader func(conn net.Conn, sni string) error
|
|
|
|
var starttlsUpgraders = map[string]starttlsUpgrader{}
|
|
|
|
func registerStartTLS(protocol string, upgrader starttlsUpgrader) {
|
|
starttlsUpgraders[protocol] = upgrader
|
|
}
|