parser

package
v0.9.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 14, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Binding

type Binding struct {
	Transport event.Transport
	Port      uint16
}

Binding is a port a parser wants traffic from.

func ParseBinding

func ParseBinding(s string) (Binding, error)

ParseBinding reads a binding written as "tcp/443" or "udp/53".

func ParseBindings

func ParseBindings(s string) ([]Binding, error)

ParseBindings reads a comma-separated list such as "tcp/80,tcp/8080".

func TCP

func TCP(ports ...uint16) []Binding

TCP and UDP build Bindings for the given ports.

func UDP

func UDP(ports ...uint16) []Binding

func (Binding) String

func (b Binding) String() string

String renders a binding in the form ParseBinding accepts.

type Direction

type Direction bool

Direction is which half of a connection some bytes belong to.

const (
	ClientToServer Direction = false
	ServerToClient Direction = true
)

func (Direction) String

func (d Direction) String() string

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

func NewPacketContext(name string, sink event.Sink, flow event.Flow, ts time.Time) *PacketContext

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 NewRegistry

func NewRegistry() *Registry

NewRegistry returns an empty registry.

func (*Registry) BPFFilter

func (r *Registry) BPFFilter() string

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) Parsers

func (r *Registry) Parsers() map[string]Parser

Parsers returns every registered parser, for diagnostics.

func (*Registry) Register

func (r *Registry) Register(p Parser) error

Register adds a parser on the ports it declares.

func (*Registry) RegisterOn

func (r *Registry) RegisterOn(p Parser, bindings []Binding) error

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

func NewStreamContext(name string, sink event.Sink, flow event.Flow, ts time.Time) *StreamContext

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL