...
 
Commits (2)
  • Alberto Bertogli's avatar
    test: Improve DATA handling in the smtpsrv fuzzer · 44140220
    Alberto Bertogli authored
    The smtpsrv fuzzer doesn't handle DATA commands particularly well:
    it will continue to read but will skip lines that have STARTTLS as
    content, and only really care for the first line due to a bug.
    
    This patch fixes the handling, and moves the logic to a separate
    function for readability.
    44140220
  • Alberto Bertogli's avatar
    testlib: Add comments and unexport unnecessary structs · fdae72f2
    Alberto Bertogli authored
    This patch contains some readability improvements to testlib: it
    adds/reformats some comments for exported functions for consistency, and
    unexports some structs that are not used outside the library.
    fdae72f2
...@@ -85,34 +85,33 @@ func Fuzz(data []byte) int { ...@@ -85,34 +85,33 @@ func Fuzz(data []byte) int {
tconn := textproto.NewConn(conn) tconn := textproto.NewConn(conn)
defer tconn.Close() defer tconn.Close()
in_data := false
scanner := bufio.NewScanner(bytes.NewBuffer(data)) scanner := bufio.NewScanner(bytes.NewBuffer(data))
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
cmd := strings.TrimSpace(strings.ToUpper(line))
// Skip STARTTLS if it happens on a non-TLS connection - the jump is // 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 // not going to happen via fuzzer, it will just cause a timeout (which
// is considered a crash). // is considered a crash).
if strings.TrimSpace(strings.ToUpper(line)) == "STARTTLS" && !mode.TLS { if cmd == "STARTTLS" && !mode.TLS {
continue continue
} }
if err = tconn.PrintfLine(line); err != nil { if err = tconn.PrintfLine(line); err != nil {
break break
} }
if in_data {
if line == "." {
in_data = false
} else {
continue
}
}
if _, _, err = tconn.ReadResponse(-1); err != nil { if _, _, err = tconn.ReadResponse(-1); err != nil {
break break
} }
in_data = strings.HasPrefix(strings.ToUpper(line), "DATA") if cmd == "DATA" {
// We just sent DATA and got a response; send the contents.
err = exchangeData(scanner, tconn)
if err != nil {
break
}
}
} }
if (err != nil && err != io.EOF) || scanner.Err() != nil { if (err != nil && err != io.EOF) || scanner.Err() != nil {
return 1 return 1
...@@ -121,6 +120,22 @@ func Fuzz(data []byte) int { ...@@ -121,6 +120,22 @@ func Fuzz(data []byte) int {
return 0 return 0
} }
func exchangeData(scanner *bufio.Scanner, tconn *textproto.Conn) error {
for scanner.Scan() {
line := scanner.Text()
if err := tconn.PrintfLine(line); err != nil {
return err
}
if line == "." {
break
}
}
// Read the "." response.
_, _, err := tconn.ReadResponse(-1)
return err
}
// //
// === Test environment === // === Test environment ===
// //
...@@ -216,7 +231,7 @@ func waitForServer(addr string) { ...@@ -216,7 +231,7 @@ func waitForServer(addr string) {
func init() { func init() {
flag.Parse() flag.Parse()
log.Default.Level = log.Debug log.Default.Level = log.Error
// Generate certificates in a temporary directory. // Generate certificates in a temporary directory.
tmpDir, err := ioutil.TempDir("", "chasquid_smtpsrv_fuzz:") tmpDir, err := ioutil.TempDir("", "chasquid_smtpsrv_fuzz:")
......
...@@ -67,6 +67,7 @@ func GetFreePort() string { ...@@ -67,6 +67,7 @@ func GetFreePort() string {
return l.Addr().String() return l.Addr().String()
} }
// WaitFor f to return true (returns true), or d to pass (returns false).
func WaitFor(f func() bool, d time.Duration) bool { func WaitFor(f func() bool, d time.Duration) bool {
start := time.Now() start := time.Now()
for time.Since(start) < d { for time.Since(start) < d {
...@@ -78,23 +79,24 @@ func WaitFor(f func() bool, d time.Duration) bool { ...@@ -78,23 +79,24 @@ func WaitFor(f func() bool, d time.Duration) bool {
return false return false
} }
type DeliverRequest struct { type deliverRequest struct {
From string From string
To string To string
Data []byte Data []byte
} }
// Courier for test purposes. Never fails, and always remembers everything. // TestCourier never fails, and always remembers everything.
type TestCourier struct { type TestCourier struct {
wg sync.WaitGroup wg sync.WaitGroup
Requests []*DeliverRequest Requests []*deliverRequest
ReqFor map[string]*DeliverRequest ReqFor map[string]*deliverRequest
sync.Mutex sync.Mutex
} }
// Deliver the given mail (saving it in tc.Requests).
func (tc *TestCourier) Deliver(from string, to string, data []byte) (error, bool) { func (tc *TestCourier) Deliver(from string, to string, data []byte) (error, bool) {
defer tc.wg.Done() defer tc.wg.Done()
dr := &DeliverRequest{from, to, data} dr := &deliverRequest{from, to, data}
tc.Lock() tc.Lock()
tc.Requests = append(tc.Requests, dr) tc.Requests = append(tc.Requests, dr)
tc.ReqFor[to] = dr tc.ReqFor[to] = dr
...@@ -102,10 +104,12 @@ func (tc *TestCourier) Deliver(from string, to string, data []byte) (error, bool ...@@ -102,10 +104,12 @@ func (tc *TestCourier) Deliver(from string, to string, data []byte) (error, bool
return nil, false return nil, false
} }
// Expect i mails to be delivered.
func (tc *TestCourier) Expect(i int) { func (tc *TestCourier) Expect(i int) {
tc.wg.Add(i) tc.wg.Add(i)
} }
// Wait until all mails have been delivered.
func (tc *TestCourier) Wait() { func (tc *TestCourier) Wait() {
tc.wg.Wait() tc.wg.Wait()
} }
...@@ -113,7 +117,7 @@ func (tc *TestCourier) Wait() { ...@@ -113,7 +117,7 @@ func (tc *TestCourier) Wait() {
// NewTestCourier returns a new, empty TestCourier instance. // NewTestCourier returns a new, empty TestCourier instance.
func NewTestCourier() *TestCourier { func NewTestCourier() *TestCourier {
return &TestCourier{ return &TestCourier{
ReqFor: map[string]*DeliverRequest{}, ReqFor: map[string]*deliverRequest{},
} }
} }
...@@ -123,5 +127,5 @@ func (c dumbCourier) Deliver(from string, to string, data []byte) (error, bool) ...@@ -123,5 +127,5 @@ func (c dumbCourier) Deliver(from string, to string, data []byte) (error, bool)
return nil, false return nil, false
} }
// Dumb courier, for when we just don't care about the result. // DumbCourier always succeeds delivery, and ignores everything.
var DumbCourier = dumbCourier{} var DumbCourier = dumbCourier{}