1package smtpserver
2
3import (
4 "fmt"
5 "net"
6 "os"
7 "path/filepath"
8 "testing"
9 "time"
10
11 "github.com/mjl-/mox/dns"
12 "github.com/mjl-/mox/mlog"
13 "github.com/mjl-/mox/mox-"
14 "github.com/mjl-/mox/queue"
15 "github.com/mjl-/mox/store"
16)
17
18// Fuzz the server. For each fuzz string, we set up servers in various connection states, and write the string as command.
19func FuzzServer(f *testing.F) {
20 f.Add("HELO remote")
21 f.Add("EHLO remote")
22 f.Add("AUTH PLAIN")
23 f.Add("MAIL FROM:<remote@remote>")
24 f.Add("RCPT TO:<local@mox.example>")
25 f.Add("DATA")
26 f.Add(".")
27 f.Add("RSET")
28 f.Add("VRFY x")
29 f.Add("EXPN x")
30 f.Add("HELP")
31 f.Add("NOOP")
32 f.Add("QUIT")
33
34 log := mlog.New("smtpserver", nil)
35 mox.Context = ctxbg
36 mox.ConfigStaticPath = filepath.FromSlash("../testdata/smtpserverfuzz/mox.conf")
37 mox.MustLoadConfig(true, false)
38 dataDir := mox.ConfigDirPath(mox.Conf.Static.DataDir)
39 os.RemoveAll(dataDir)
40 acc, err := store.OpenAccount(log, "mjl")
41 if err != nil {
42 f.Fatalf("open account: %v", err)
43 }
44 defer acc.Close()
45 err = acc.SetPassword(log, "testtest")
46 if err != nil {
47 f.Fatalf("set password: %v", err)
48 }
49 defer store.Switchboard()()
50 err = queue.Init()
51 if err != nil {
52 f.Fatalf("queue init: %v", err)
53 }
54 defer queue.Shutdown()
55
56 comm := store.RegisterComm(acc)
57 defer comm.Unregister()
58
59 var cid int64 = 1
60
61 var fl *os.File
62 if false {
63 fl, err = os.Create("fuzz.log")
64 if err != nil {
65 f.Fatalf("fuzz log")
66 }
67 defer fl.Close()
68 }
69 flog := func(err error, msg string) {
70 if fl != nil && err != nil {
71 fmt.Fprintf(fl, "%s: %v\n", msg, err)
72 }
73 }
74
75 f.Fuzz(func(t *testing.T, s string) {
76 run := func(cmds []string) {
77 limitersInit() // Reset rate limiters.
78 serverConn, clientConn := net.Pipe()
79 defer serverConn.Close()
80 defer clientConn.Close()
81
82 go func() {
83 err := clientConn.SetDeadline(time.Now().Add(time.Second))
84 flog(err, "set client deadline")
85 _, err = clientConn.Read(make([]byte, 1024))
86 flog(err, "read ehlo")
87 for _, cmd := range cmds {
88 _, err = clientConn.Write([]byte(cmd + "\r\n"))
89 flog(err, "write command")
90 _, err = clientConn.Read(make([]byte, 1024))
91 flog(err, "read response")
92 }
93 _, err = clientConn.Write([]byte(s + "\r\n"))
94 flog(err, "write test command")
95 _, err = clientConn.Read(make([]byte, 1024))
96 flog(err, "read test response")
97 clientConn.Close()
98 serverConn.Close()
99 }()
100
101 resolver := dns.MockResolver{}
102 const submission = false
103 err := serverConn.SetDeadline(time.Now().Add(time.Second))
104 flog(err, "set server deadline")
105 serve("test", cid, dns.Domain{ASCII: "mox.example"}, nil, serverConn, resolver, submission, false, 100<<10, false, false, false, nil, 0)
106 cid++
107 }
108
109 run([]string{})
110 run([]string{"EHLO remote"})
111 run([]string{"EHLO remote", "MAIL FROM:<remote@example.org>"})
112 run([]string{"EHLO remote", "MAIL FROM:<remote@example.org>", "RCPT TO:<mjl@mox.example>"})
113 // todo: submission with login
114 })
115}
116