Documentation
¶
Overview ¶
Package proto implements the clamd wire protocol primitives: z-format command encoding, INSTREAM chunk framing, and response parsing.
The package is transport-agnostic: it operates on io.Reader/io.Writer and leaves connection management, deadlines, and context handling to the caller.
Index ¶
- Constants
- Variables
- func EncodeCommand(name string) []byte
- func ReadBlock(br *bufio.Reader, max int) (string, error)
- func ReadLine(br *bufio.Reader, max int) (string, error)
- func StreamAll(w io.Writer, r io.Reader, chunkSize int, maxBytes int64) (int64, error)
- type Outcome
- type ScanResponse
- type SinkError
- type SourceError
Constants ¶
const ( // MaxChunkSize is a sanity cap for the INSTREAM chunk size. Chunk // lengths are encoded as uint32, but allowing arbitrarily large chunks // would only waste memory without improving throughput. MaxChunkSize = 16 << 20 // 16 MiB // DefaultChunkSize is used when the caller passes a non-positive chunk // size. Callers are expected to validate configuration upfront; this is // a defensive fallback only. DefaultChunkSize = 32 << 10 // 32 KiB )
const ( // MaxLineResponse bounds single-line responses (scan verdicts, PONG, // VERSION, ...). A hostile or broken server cannot make us buffer more. MaxLineResponse = 4 << 10 // 4 KiB // MaxBlockResponse bounds multi-line responses (STATS). MaxBlockResponse = 1 << 20 // 1 MiB )
Variables ¶
var ErrMalformedReply = errors.New("multi-line reply to a single-line command")
ErrMalformedReply indicates a multi-line reply to a command that must produce a single line. All commands are sent in z form, so a reply is one NUL-terminated unit; content with embedded newlines cannot be classified safely (a hostile "stream: <sig> FOUND\nstream: OK" must not be judged by either single line) and is treated as a protocol violation.
var ErrResponseTooLarge = errors.New("response exceeds read limit")
ErrResponseTooLarge indicates the server sent a response exceeding the read limit. Treated as a protocol violation (fail-closed).
var ErrSizeLimitExceeded = errors.New("stream size limit exceeded")
ErrSizeLimitExceeded is returned by StreamAll when the source would exceed the configured byte limit. The chunk that would cross the limit is not written to the sink.
Functions ¶
func EncodeCommand ¶
EncodeCommand returns the z-format (NUL-terminated) encoding of a clamd command, e.g. EncodeCommand("PING") == "zPING\x00". The z form is used for every command so that responses are unambiguously NUL-delimited.
func ReadBlock ¶
ReadBlock reads a multi-line response (STATS) terminated by NUL or EOF, up to max bytes. Newlines are preserved as part of the content.
func ReadLine ¶
ReadLine reads a single-line response terminated by NUL (z-format), up to max bytes of content. Trailing '\r' and '\n' are trimmed, but an embedded newline fails with ErrMalformedReply. EOF after at least one byte returns the data read so far (clamd may close the connection right after replying); EOF with no data is returned as io.EOF so callers can classify "closed without response".
func StreamAll ¶
StreamAll reads r to EOF and writes it to w as INSTREAM chunks: a 4-byte big-endian length prefix followed by the payload, terminated by a zero-length chunk. It returns the number of payload bytes written.
maxBytes limits the payload size; a negative value means unlimited. When the source would exceed maxBytes, StreamAll stops before writing the offending chunk and returns ErrSizeLimitExceeded, so no truncated payload is ever presented to clamd as a complete stream.
Read failures are wrapped in *SourceError, write failures in *SinkError. On any error the zero-length terminator is NOT written; the caller must close the connection so clamd cannot treat a partial stream as complete.
Types ¶
type Outcome ¶
type Outcome uint8
Outcome classifies a scan response. The zero value is OutcomeUnknown so that an unhandled or malformed response can never read as "clean".
type ScanResponse ¶
type ScanResponse struct {
Outcome Outcome
Signature string // set when Outcome == OutcomeInfected
Message string // ERROR message (Outcome == OutcomeError) or raw line (OutcomeUnknown)
SizeLimit bool // true when the ERROR indicates a size limit violation
}
ScanResponse is the parsed form of a scan verdict line.
func ParseScanResponse ¶
func ParseScanResponse(line string) ScanResponse
ParseScanResponse classifies a single scan response line.
The parser is deliberately prefix-agnostic: clamd historically used different reply prefixes ("stream: ...", "instream (local): ...", or a file path for path-based scans), so classification relies on the reply suffix only:
"<prefix>: <signature> FOUND" -> OutcomeInfected "<message> ERROR" -> OutcomeError "OK" or "<stream prefix>: OK" -> OutcomeClean anything else -> OutcomeUnknown (fail-closed)
FOUND is checked before ERROR and OK: when a response is ambiguous the parser must never prefer the more permissive classification. Signature names may contain spaces; only the final " FOUND" token is stripped.
type SinkError ¶
type SinkError struct {
Err error
}
SinkError wraps a failure to write to clamd. Callers should attempt to read a pending ERROR response after observing a SinkError, because clamd replies and closes the connection when a stream exceeds StreamMaxLength.
type SourceError ¶
type SourceError struct {
Err error
}
SourceError wraps a failure to read from the caller-supplied data source (e.g. the io.Reader passed to Scan). It never indicates a clamd problem.
func (*SourceError) Error ¶
func (e *SourceError) Error() string
func (*SourceError) Unwrap ¶
func (e *SourceError) Unwrap() error