Documentation
¶
Overview ¶
Package parser defines the contract every protocol parser implements and the registry the engine uses to dispatch traffic to them.
There are two kinds of parser, because there are two kinds of protocol:
- PacketParser sees one datagram at a time. Use it for protocols where a message never spans packets: NTP, DNS over UDP, DHCP, SNMP.
- StreamParser sees a reassembled, in-order byte stream per direction. Use it for anything over TCP, where message boundaries have nothing to do with segment boundaries: HTTP, TLS, SMTP.
A parser declares which ports it wants via Bindings; the engine handles all capture, decoding, and reassembly.
Index ¶
- type Binding
- type Direction
- type PacketContext
- type PacketParser
- type Parser
- type Registry
- func (r *Registry) BPFFilter() string
- func (r *Registry) LookupPacket(t event.Transport, srcPort, dstPort uint16) PacketParser
- func (r *Registry) LookupStream(srcPort, dstPort uint16) (p StreamParser, flip bool)
- func (r *Registry) Parsers() map[string]Parser
- func (r *Registry) Register(p Parser) error
- func (r *Registry) RegisterOn(p Parser, bindings []Binding) error
- type StreamContext
- type StreamHandler
- type StreamParser
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Binding ¶
Binding is a port a parser wants traffic from.
func ParseBinding ¶
ParseBinding reads a binding written as "tcp/443" or "udp/53".
func ParseBindings ¶
ParseBindings reads a comma-separated list such as "tcp/80,tcp/8080".
type PacketContext ¶
type PacketContext struct {
// Flow is the packet's 5-tuple, as observed (not normalized).
Flow event.Flow
// Timestamp is when the packet was captured.
Timestamp time.Time
// Tunnel is the encapsulation the packet arrived in, or nil if it was
// seen directly. The engine sets it; parsers do not need to look at it.
Tunnel *event.Tunnel
// contains filtered or unexported fields
}
PacketContext carries per-packet metadata to a PacketParser and collects the events it emits.
func NewPacketContext ¶
NewPacketContext builds a context. The engine calls this; parsers only need it in tests.
func (*PacketContext) Emit ¶
func (c *PacketContext) Emit(kind string, fields any)
Emit records one parsed message. kind distinguishes message types within a parser ("request", "response"); fields is the protocol-specific payload.
type PacketParser ¶
type PacketParser interface {
Parser
// ParsePacket is called with the transport payload of a single packet.
// Returning an error records a parse failure; it does not stop capture.
ParsePacket(pc *PacketContext, payload []byte) error
}
PacketParser parses self-contained datagrams.
type Parser ¶
type Parser interface {
// Name identifies the parser and appears on every event it emits.
Name() string
// Bindings are the ports this parser should receive traffic on.
Bindings() []Binding
}
Parser is the part of the contract both parser kinds share.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry maps ports to the parsers that handle them.
func (*Registry) BPFFilter ¶
BPFFilter builds a capture filter matching every registered binding, so the kernel discards traffic no parser would look at.
func (*Registry) LookupPacket ¶
func (r *Registry) LookupPacket(t event.Transport, srcPort, dstPort uint16) PacketParser
LookupPacket finds the parser for a datagram. It prefers the destination port, so a client's ephemeral port never shadows a server port.
func (*Registry) LookupStream ¶
func (r *Registry) LookupStream(srcPort, dstPort uint16) (p StreamParser, flip bool)
LookupStream finds the parser for a TCP connection. flip reports that the well-known port was the source, meaning the connection key is oriented server to client and the engine must invert directions.
func (*Registry) RegisterOn ¶
RegisterOn adds a parser on the given ports instead of the ones it declares, which is how a deployment binds a parser where its protocol actually runs.
It reports an error on a duplicate name or on two parsers claiming the same port, since either is a wiring mistake rather than something to resolve silently.
type StreamContext ¶
type StreamContext struct {
// Flow is the connection's 5-tuple oriented client to server.
Flow event.Flow
// Tunnel is the encapsulation the connection arrived in, or nil. It is
// taken from the first packet seen, since a connection does not move
// between tunnels.
Tunnel *event.Tunnel
// contains filtered or unexported fields
}
StreamContext carries per-connection metadata to a StreamParser.
Unlike a packet, a stream has no single timestamp, so Emit uses the capture time of the most recent segment delivered on the connection. That is an approximation: a message assembled from several segments is stamped with the arrival of the last one contributing to it.
func NewStreamContext ¶
NewStreamContext builds a context. The engine calls this; parsers only need it in tests.
func (*StreamContext) Emit ¶
func (c *StreamContext) Emit(dir Direction, kind string, fields any)
Emit records one parsed message travelling in dir. The event's flow is oriented to match dir, so a response reads as server to client.
Emit is safe to call from either direction's goroutine.
func (*StreamContext) Now ¶
func (c *StreamContext) Now() time.Time
Now returns the capture time of the most recent segment on this connection.
func (*StreamContext) Observe ¶
func (c *StreamContext) Observe(ts time.Time)
Observe records the capture time of newly delivered bytes. The engine calls this as segments arrive.
type StreamHandler ¶
type StreamHandler interface {
// Handle is called at most once per direction, each on its own goroutine,
// with a reader over that direction's reassembled bytes. It should read
// until io.EOF and then return. Reads block while waiting for more packets,
// so an implementation must not hold locks across a Read.
Handle(dir Direction, r io.Reader)
// Close is called after both directions have finished. It is the place to
// flush any state accumulated across directions.
Close()
}
StreamHandler processes one TCP connection.
type StreamParser ¶
type StreamParser interface {
Parser
// NewStream is called once per TCP connection, before any data arrives.
NewStream(sc *StreamContext) StreamHandler
}
StreamParser parses reassembled TCP connections.