package mailstore import ( "encoding/binary" "io" "net" "testing" ) // fakeClamd stands in for a real clamd instance: reads the INSTREAM protocol exactly // as a real one would (command, length-prefixed chunks, zero-length terminator), // reassembles the full payload so the test can assert on exactly what ScanVirus sent, // then writes back the given canned reply. func fakeClamd(t *testing.T, reply string) (addr string, gotPayload chan []byte) { t.Helper() ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } t.Cleanup(func() { ln.Close() }) gotPayload = make(chan []byte, 1) go func() { conn, err := ln.Accept() if err != nil { return } defer conn.Close() cmd := make([]byte, len("zINSTREAM\x00")) if _, err := io.ReadFull(conn, cmd); err != nil { return } var payload []byte for { var lenBuf [4]byte if _, err := io.ReadFull(conn, lenBuf[:]); err != nil { return } n := binary.BigEndian.Uint32(lenBuf[:]) if n == 0 { break } chunk := make([]byte, n) if _, err := io.ReadFull(conn, chunk); err != nil { return } payload = append(payload, chunk...) } gotPayload <- payload conn.Write([]byte(reply + "\x00")) }() return ln.Addr().String(), gotPayload } func TestScanVirusCleanReply(t *testing.T) { addr, gotPayload := fakeClamd(t, "stream: OK") infected, sig, err := ScanVirus(addr, []byte("hello world")) if err != nil { t.Fatal(err) } if infected || sig != "" { t.Fatalf("infected=%v sig=%q, want clean", infected, sig) } if got := <-gotPayload; string(got) != "hello world" { t.Fatalf("clamd received %q, want %q — the length-prefixed chunk protocol is broken", got, "hello world") } } func TestScanVirusInfectedReply(t *testing.T) { addr, _ := fakeClamd(t, "stream: Eicar-Test-Signature FOUND") infected, sig, err := ScanVirus(addr, []byte("X5O!P%@AP")) if err != nil { t.Fatal(err) } if !infected || sig != "Eicar-Test-Signature" { t.Fatalf("infected=%v sig=%q, want infected with the parsed signature name", infected, sig) } } func TestScanVirusErrorReply(t *testing.T) { addr, _ := fakeClamd(t, "stream: Access denied ERROR") if _, _, err := ScanVirus(addr, []byte("x")); err == nil { t.Fatal("expected an error for a clamd-reported ERROR reply") } } func TestScanVirusUnreachable(t *testing.T) { if _, _, err := ScanVirus("127.0.0.1:1", []byte("x")); err == nil { t.Fatal("expected an error dialing an unreachable clamd") } } func TestScanVirusChunksLargePayload(t *testing.T) { addr, gotPayload := fakeClamd(t, "stream: OK") large := make([]byte, 20000) // spans multiple 8192-byte chunks for i := range large { large[i] = byte(i % 251) } if _, _, err := ScanVirus(addr, large); err != nil { t.Fatal(err) } got := <-gotPayload if len(got) != len(large) { t.Fatalf("clamd received %d bytes, want %d — multi-chunk reassembly is broken", len(got), len(large)) } for i := range large { if got[i] != large[i] { t.Fatalf("byte %d mismatch: got %d want %d", i, got[i], large[i]) } } }