paclo.decode-ext lets you annotate or transform decoded packets immediately after decode,
without changing the base parser (parse/packet->clj).
m -> m' where m is a packet map from (packets {:decode? true ...}).:decoded exists and :decode-error is absent.m -> m') is stable.register!, unregister!, installed, and apply! behavior is part of the public API contract.(require '[paclo.decode-ext :as dx])
(dx/register! ::my-hook
(fn [m]
(if (= :udp (get-in m [:decoded :l3 :l4 :type]))
(assoc-in m [:decoded :note] "hello-udp")
m)))
(dx/unregister! ::my-hook)
(dx/installed)
Full decode runs extensions before the user :xform, through the same
packet-xf transformation used by packets, reduce-packets,
reduce-packets-report, and managed start-capture / capture-packets.
:extensions to use the current global registry on each packet.
Registering or unregistering after constructing a transducer affects it.:extensions [] to run no extensions, including global hooks.fn?) are validated at construction. Explicit
nil, lists, maps, keywords, strings and non-function elements throw ex-info.
Explicit selection requires :decode? true; raw and :decode-mode :flow
reject it, even for an empty vector, before source open.:decoded is present and :decode-error is absent.
Exceptions and non-map results, including nil, preserve the preceding map.
Later functions continue. This is the same failure isolation as global hooks;
the default :extension-error-mode :ignore preserves these outputs.
Opt into :report to append EDN diagnostics for exceptions/non-map results,
with zero-based indices in execution order (also for global hooks).
See the canonical diagnostics contract
and executable example.
Existing decode-error counters and capture :error-mode remain separate.The supported ordinary annotation functions are dns-ext/annotate-dns and
tls-ext/annotate-tls-sni. Existing register! functions install those same
functions. DNS retains summary and best-effort question fields; TLS retains
best-effort SNI and ALPN without TCP reassembly. Both leave unrelated packets
unchanged and can be called directly without registering. When the selected
functions are pure, the same input and vector produce deterministic output.
(require '[paclo.core :as core]
'[paclo.proto.dns-ext :as dns-ext]
'[paclo.proto.tls-ext :as tls-ext])
(let [note (fn [m] (update m :steps (fnil conj []) :annotated))]
(core/reduce-packets
{:path "test/resources/dns-sample.pcap"
:decode? true
:extensions [dns-ext/annotate-dns tls-ext/annotate-tls-sni note note]
:xform (map #(vector (:steps %)
(get-in % [:decoded :l3 :l4 :app :summary])))}
conj []))
These APIs are additions in v1.4.0 (P03); v1.3.0 uses global hooks.
Distribute raw packets and apply a separate packet-xf in each consumer.
Blocking branches must drain concurrently; each consumer owns its branch.
The finite input below contains DNS and TLS PCAP data:
(require '[paclo.core :as core]
'[paclo.stream :as stream]
'[paclo.proto.dns-ext :as dns-ext]
'[paclo.proto.tls-ext :as tls-ext])
(let [raw (concat (core/packets {:path "test/resources/dns-sample.pcap"})
(core/packets {:path "test/resources/tls-sni-alpn-sample.pcap"}))]
(with-open [fanout (stream/fan-out
raw {:dns {:buffer-mode :blocking :buffer-cap 1}
:tls {:buffer-mode :blocking :buffer-cap 1}})
dns-branch (stream/branch fanout :dns)
tls-branch (stream/branch fanout :tls)]
(let [dns-result (future
(into [] (core/packet-xf
{:decode? true :extensions [dns-ext/annotate-dns]})
dns-branch))
tls-result (future
(into [] (core/packet-xf
{:decode? true :extensions [tls-ext/annotate-tls-sni]})
tls-branch))]
(try
{:dns @dns-result :tls @tls-result}
(finally
(future-cancel dns-result)
(future-cancel tls-result))))))
Packet byte arrays are mutable and shared across branches. Treat published
bytes as read-only; these annotations do not mutate them. For a managed source,
own capture, fan-out, and branches together with with-open and use :cancel!
to unblock the source as described in the usage guide.
The fan-out buffering, cancellation and single-consumer contracts are unchanged.
(require '[paclo.proto.dns-ext :as dns-ext])
(dns-ext/register!)
;; Adds [:decoded :l3 :l4 :app :summary] to DNS packets
Repository-local CLI runs that use DNS extension should include :dns-ext:
clojure -M:dev:dns-ext -m examples.dns-topn test/resources/dns-sample.pcap
When Paclo is consumed as a dependency artifact, paclo.proto.dns-ext is included.
Extract SNI from TLS ClientHello on a best-effort basis (single-segment, no stream reassembly).
(require '[paclo.core :as core]
'[paclo.proto.tls-ext :as tls-ext])
(tls-ext/register!)
(into []
(comp
(filter #(= :tls (get-in % [:decoded :l3 :l4 :app :type])))
(map #(select-keys (get-in % [:decoded :l3 :l4 :app]) [:sni :summary])))
(core/packets {:path "tls-sample.pcap"
:filter "tcp and port 443"
:decode? true}))
CLI example:
clojure -M:dev -m examples.tls-sni-scan tls-sample.pcap 'tcp and port 443' 10 jsonl
:my.ns/hook).Full decode skips all extension hooks when an explicit :linktype is unsupported,
including global registration and explicit :extensions with either error mode.
Raw reads still expose those bytes and their native DLT. See the
link-type dispatch contract.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |