Internals Relay Control Decoding - NormB/sipnab GitHub Wiki
How sipnab reads a relay's control plane. It also covers what sipnab refuses to conclude from it.
src/relay/ defines what a relay implementation owes:
- decode a control message
- say whether a command creates media
- yield endpoints carrying an
EndpointAssertion - report its own authentication status
rtpengine's ng protocol and rtpproxy's text protocol both sit behind it.
Nothing above the seam learns either vendor's name.
A search of the rtpproxy tree for bencode returns nothing.
So the ng decoder covers none of this.
- The
protos/directory is a false lead. - This page names it so nobody re-investigates it.
-
rtpp_request.protodescribes an internal module interface, not a second wire dialect.
A command and a reply both begin with the same cookie.
- The destination port says which way a datagram went.
- The decoder parses the two grammars separately.
The parser also refuses a reply on content. Both layers earn their place.
- A live relay's
Ireply is five lines beginning<cookie> sessions created: 0. -
sessionsstarts withs, the stop-play letter. - So a content-blind reading made it a confident
Scommand carrying fourteen arguments. - Three rules in
rtpp_command_parse.crefuse it:- a command is one line
- stop-play accepts no modifiers
- stop-play takes three or four arguments rather than fifteen
Two ceilings bound what a sniffed datagram can make the decoder do. Both refuse rather than truncate.
- The decoder drops a control message that exceeds either.
- The reason: a half-read command names a call from whatever sat in the position it stopped at.
| Constant | Value | What it bounds |
|---|---|---|
MAX_DATAGRAM |
8192 | Longest control datagram the decoder examines. It drops anything larger unread. |
MAX_TOKEN |
512 | Longest single token — a cookie, a verb, an argument. rtpproxy's own buffers are far smaller; this exists so an unterminated token cannot make the decoder allocate. |
A text protocol has denial-of-service shapes that a length-prefixed binary one does not. So these stay fixed rather than following the input.
-
No SDP.
- Unlike
ng, the signaling element parses and rewrites SDP itself. - It tells the relay only addresses and ports.
- Reporting an SDP byte count would invent one.
- Unlike
-
No call-id on
V,I,XorG.- Those carry
has_call_id = 0. - Reading the first argument as a call-id would name a call from whatever sat there.
- Those carry
-
No credential of any kind.
- Nothing authenticates a sniffed control datagram.
- So the decoder reports it as a bare datagram and never as an encapsulated one.
-
The operator names the socket.
- rtpproxy's default control socket is a UNIX socket. A passive capture cannot see it at all.
- A UDP socket is opt-in (
-s udp:ADDR[:PORT]). - Without a port it listens on 22222
(
rtpproxy.8,CPORTinsrc/rtpp_defines.h). - sipnab reads rtpproxy's control only on the address and port given to
--rtpproxy-control. - It never assumes 22222.
- Every datagram on an assumed port would be a candidate control message.
- A believed one names a call.
R and C create recording or forking streams.
- Attributing one as an ordinary participant invents somebody the call never had.
- So the two are separate at the type level rather than in a caller's memory.
- They also stay out of the unattributed-media tally. The recording spool owns that tally.