// Fuzz testing for package smtpsrv. Based on server_test. package smtpsrv import ( "bufio" "bytes" "crypto/tls" "fmt" "net" "net/textproto" "strings" "testing" ) func fuzzConnection(t *testing.T, modeI int, data []byte) { var mode SocketMode addr := "" switch modeI { case 0: mode = ModeSMTP addr = smtpAddr case 1: mode = ModeSubmission addr = submissionAddr case 2: mode = ModeSubmissionTLS addr = submissionTLSAddr default: mode = ModeSMTP addr = smtpAddr } var err error var conn net.Conn if mode.TLS { conn, err = tls.Dial("tcp", addr, tlsConfig) } else { conn, err = net.Dial("tcp", addr) } if err != nil { panic(fmt.Errorf("failed to dial: %v", err)) } defer conn.Close() tconn := textproto.NewConn(conn) defer tconn.Close() scanner := bufio.NewScanner(bytes.NewBuffer(data)) for scanner.Scan() { line := scanner.Text() cmd := strings.TrimSpace(strings.ToUpper(line)) // Skip STARTTLS if it happens on a non-TLS connection - the jump is // not going to happen via fuzzer, it will just cause a timeout (which // is considered a crash). if cmd == "STARTTLS" && !mode.TLS { continue } if err = tconn.PrintfLine("%s", line); err != nil { break } if _, _, err = tconn.ReadResponse(-1); err != nil { break } if cmd == "DATA" { // We just sent DATA and got a response; send the contents. err = exchangeData(scanner, tconn) if err != nil { break } } } } func FuzzConnection(f *testing.F) { f.Fuzz(fuzzConnection) } func exchangeData(scanner *bufio.Scanner, tconn *textproto.Conn) error { for scanner.Scan() { line := scanner.Text() if err := tconn.PrintfLine("%s", line); err != nil { return err } if line == "." { break } } // Read the "." response. _, _, err := tconn.ReadResponse(-1) return err }