// build gofuzz package guerrilla import ( "bytes" "fmt" "github.com/flashmob/go-guerrilla/backends" "github.com/flashmob/go-guerrilla/log" "github.com/flashmob/go-guerrilla/mail" "github.com/flashmob/go-guerrilla/mocks" "io" "sync" "time" ) var logOff log.Logger var fuzzServer *server var envelopePool *mail.Pool func init() { sc := getFuzzServerConfig() logOff, _ = log.GetLogger(sc.LogFile, log.ErrorLevel.String()) fuzzServer = getFuzzServer(sc) isFuzzDebug = false envelopePool = mail.NewPool(sc.MaxClients) } func getFuzzServerConfig() *ServerConfig { sc := &ServerConfig{ IsEnabled: true, // Hostname: "fuzzme.test.com", MaxSize: 1024, // smtp message max size TLS: ServerTLSConfig{ PrivateKeyFile: "./tests/mail.guerrillamail.com.key.pem", PublicKeyFile: "./tests/mail.guerrillamail.com.cert.pem", StartTLSOn: true, AlwaysOn: false, }, Timeout: 5, ListenInterface: "127.0.0.1:2529", MaxClients: 3000, LogFile: "off", } return sc } // getMockServer gets a new server using sc. Server will be using a using the dummy backend // RCP TO command only allows test.com host func getFuzzServer(sc *ServerConfig) *server { var logOpenError error var mainlog log.Logger mainlog, logOpenError = log.GetLogger(sc.LogFile, log.ErrorLevel.String()) if logOpenError != nil { mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface) } backend, err := backends.New(backends.BackendConfig{ "log_received_mails": true, "save_process_": "HeadersParser|Header|Debugger"}, mainlog) if err != nil { //t.Error("new dummy backend failed because:", err) } backend.Start() server, err := newServer(sc, backend, mainlog) if err != nil { //t.Error("new server failed because:", err) } else { server.setAllowedHosts([]string{"test.com"}) } return server } var mockClient *client var isFuzzDebug bool // Fuzz passes the data to the mock connection // Data is random input generated by go-fuzz, note that in most cases it is invalid. // The function must return 1 if the fuzzer should increase priority of the given input during subsequent // fuzzing (for example, the input is lexically correct and was parsed successfully); -1 if the input must // not be added to corpus even if gives new coverage; and 0 otherwise func Fuzz(data []byte) int { var wg sync.WaitGroup // grab a new mocked tcp connection, it consists of two pipes (io.Pipe) conn := mocks.NewConn() // Get a client from the pool poolable, err := fuzzServer.clientPool.Borrow(conn.Server, 1, logOff, envelopePool) if c, ok := poolable.(*client); !ok { panic("cannot borrow from pool") } else { mockClient = c } defer func() { conn.Close() // wait for handleClient to exit wg.Wait() // return to the pool fuzzServer.clientPool.Return(mockClient) }() wg.Add(1) go func() { fuzzServer.handleClient(mockClient) wg.Done() }() // read in the greeting in := newSMTPBufferedReader(conn.Client) s, _ := in.ReadString('\n') if isFuzzDebug { fmt.Println("Read", s) } // Feed the connection with fuzz data (we are the _client_ end of the connection) if _, err = io.Copy(conn.Client, bytes.NewReader(data)); err != nil { fmt.Println(err) return 1 } // allow handleClient to process time.Sleep(time.Millisecond + 10) if mockClient.bufout.Buffered() == 0 { // nothing to read - no complete commands sent? return 1 } var e error var z string for ; e == nil; z, e = in.ReadString('\n') { fmt.Println("Read", z, mockClient.bufout.Buffered()) if mockClient.bufout.Buffered() == 0 { break } } return 1 }