Documentation
¶
Overview ¶
Package sdp parses and generates minimal audio SDP bodies for SIP/VoIP.
It supports RTP/AVP, RTP/AVPF, and RTP/SAVP(F) audio lines with a=rtpmap, static payload types (0/8/9) when rtpmap is omitted, and parses RFC 4568 a=crypto SDES attributes when present.
NormalizeBody trims and normalizes CRLF/LF before Parse so offers from heterogeneous UAs parse consistently.
Production SIP stacks import this package directly for Parse / Generate / codec negotiate helpers.
After you map SDP codecs to names (pcmu, pcma, opus, g722), use pkg/media/encoder.CreateDecode / CreateEncode — do not reimplement those codecs in sip1.
Index ¶
- Constants
- func DecodeSDESInline(keyParams string) (masterKey, masterSalt []byte, err error)
- func FormatCryptoLine(tag uint32, suite string, masterKey, masterSalt []byte) (string, error)
- func FormatFingerprint(fp Fingerprint) string
- func FormatFingerprintLine(fp Fingerprint) string
- func FormatSetupLine(role DTLSRole) string
- func Generate(localIP string, localPort int, codecs []Codec) string
- func GenerateWithProto(localIP string, localPort int, proto string, codecs []Codec) string
- func GenerateWithProtoExtras(localIP string, localPort int, proto string, codecs []Codec, ...) string
- func IsDTLSTransport(proto string) bool
- func NormalizeBody(body string) string
- func PionProfileForSuite(suite string) (srtp.ProtectionProfile, bool)
- func VerifyDTLSCertFingerprint(peerCertDER []byte, advertised []Fingerprint) error
- type Codec
- type CryptoOffer
- type DTLSRole
- type Fingerprint
- type Info
Constants ¶
const ( // SuiteAESCM128HMACSHA180 is the common SDES suite string for SRTP (RFC 4568). // 128-bit AES-CM confidentiality + 80-bit HMAC-SHA1 auth tag — the // default everywhere. WebRTC, Asterisk, FreeSWITCH all default to this. SuiteAESCM128HMACSHA180 = "AES_CM_128_HMAC_SHA1_80" // SuiteAESCM128HMACSHA132 is the bandwidth-saving variant: same cipher // + key length, but 32-bit auth tag (RFC 4568 / RFC 3711 §4.2.1). // Common on Cisco CUCM and some Avaya softphones — interop requires // us to accept it on offer and produce it on answer when the peer // didn't list HMAC_SHA1_80. SuiteAESCM128HMACSHA132 = "AES_CM_128_HMAC_SHA1_32" )
const DefaultSessionName = "SoulNexus SIP"
DefaultSessionName is the fixed s= line used by Generate / GenerateWithProto.
Variables ¶
This section is empty.
Functions ¶
func DecodeSDESInline ¶
DecodeSDESInline extracts SRTP master key and salt from an RFC 4568 key-params field whose first token is inline:BASE64... Optional lifetime/MKI suffix after '|' is ignored.
func FormatCryptoLine ¶
FormatCryptoLine builds one complete SDP line: a=crypto:<tag> <suite> inline:<base64(key||salt)>.
func FormatFingerprint ¶
func FormatFingerprint(fp Fingerprint) string
FormatFingerprint renders one `a=fingerprint:` line value (without the leading "a=fingerprint:"). Returns "" when the fingerprint is invalid.
func FormatFingerprintLine ¶
func FormatFingerprintLine(fp Fingerprint) string
FormatFingerprintLine renders a complete `a=fingerprint:` SDP attribute line (no trailing CRLF — caller's emitter adds it). Returns "" when the fingerprint is malformed; callers MUST NOT fall through to a non-fingerprinted DTLS-SRTP answer (RFC 5763 §3 requires the fingerprint to commit to the cert).
func FormatSetupLine ¶
FormatSetupLine renders a complete `a=setup:` SDP attribute line. Returns "" when role is invalid.
func GenerateWithProto ¶
GenerateWithProto builds minimal audio SDP with a given m=audio proto (e.g. RTP/AVP, RTP/AVPF, RTP/SAVPF).
func GenerateWithProtoExtras ¶
func GenerateWithProtoExtras(localIP string, localPort int, proto string, codecs []Codec, extraLines []string) string
GenerateWithProtoExtras is like GenerateWithProto but appends extra SDP lines (e.g. a=crypto) after fmtp.
func IsDTLSTransport ¶
IsDTLSTransport reports whether the m= proto string indicates DTLS-SRTP per RFC 5764 §8.
Accepted forms:
- UDP/TLS/RTP/SAVP — RFC 5764 §9 audio/video
- UDP/TLS/RTP/SAVPF — with feedback (WebRTC default)
func NormalizeBody ¶
NormalizeBody trims whitespace and collapses line endings to LF so parsing is stable across CRLF/LF peers.
func PionProfileForSuite ¶
func PionProfileForSuite(suite string) (srtp.ProtectionProfile, bool)
PionProfileForSuite maps an SDES suite string to the pion/srtp profile enum. Returns ok=false when unsupported.
The two AES_CM_128 suites use identical key/salt lengths (16 / 14 bytes — RFC 3711 §4.1.1); only the auth tag length differs. So SDP key-material parsing/rendering is suite-agnostic; only the SRTP context creation needs the right profile.
func VerifyDTLSCertFingerprint ¶
func VerifyDTLSCertFingerprint(peerCertDER []byte, advertised []Fingerprint) error
VerifyDTLSCertFingerprint enforces the RFC 5763 §3 binding between SDP `a=fingerprint` and the cert the peer presented in the DTLS handshake. peerCertDER is the DER-encoded leaf cert (typically conn.ConnectionState().PeerCertificates[0]); advertised is everything we parsed from the peer's SDP.
Returns nil when at least one advertised fingerprint (any supported hash algorithm) matches the peer's actual cert digest. Returns a diagnostic error otherwise — caller MUST tear down the call when this fails because without the binding the cert is uncommitted and a passive MITM owns the media.
Currently only sha-256 is implemented; sha-224/sha-384/sha-512 silently skip — peers that advertise ONLY those (extremely rare in practice) will fail verification. Adding more hashes is a one-liner in hashCertWithAlg.
Types ¶
type Codec ¶
Codec is one RTP payload mapping (typically from a=rtpmap).
func DefaultOutboundOfferCodecs ¶
func DefaultOutboundOfferCodecs() []Codec
DefaultOutboundOfferCodecs is the standard outbound INVITE audio preference list: PCMA first for carrier interoperability, then PCMU, G.722, Opus, telephone-event.
func PickTelephoneEventFromOffer ¶
PickTelephoneEventFromOffer picks a telephone-event codec; prefers clock rate match when matchClockRate > 0.
func TransferAgentBridgeOfferCodecs ¶
func TransferAgentBridgeOfferCodecs() []Codec
TransferAgentBridgeOfferCodecs is the INVITE offer for the human/agent leg after transfer (narrowband-first to simplify PCM bridging).
type CryptoOffer ¶
type CryptoOffer struct {
Tag uint32
Suite string
KeyParams string // raw key-params field (e.g. "inline:...")
}
CryptoOffer is one a=crypto line from SDP (RFC 4568), scoped to the current audio m-line.
func PickAESCM128Offer ¶
func PickAESCM128Offer(offers []CryptoOffer) (CryptoOffer, bool)
PickAESCM128Offer returns the first crypto offer using AES_CM_128_HMAC_SHA1_80, if any. Kept as a thin shim for callers that ONLY want the WebRTC-preferred suite — most production code should use PickSupportedSDESOffer instead.
func PickSupportedSDESOffer ¶
func PickSupportedSDESOffer(offers []CryptoOffer) (CryptoOffer, bool)
PickSupportedSDESOffer picks the most-preferred SDES offer this stack can handle from a peer's a=crypto list. Preference order matches our offer side:
- AES_CM_128_HMAC_SHA1_80 — interop default, WebRTC-compatible
- AES_CM_128_HMAC_SHA1_32 — Cisco / Avaya bandwidth-saving form
The first match by preference wins, regardless of the order the peer listed them. Returns ok=false when nothing is acceptable — caller MUST 488 the call (we can't downgrade a SAVP m= block to plain RTP per RFC 4568 §6.2).
type DTLSRole ¶
type DTLSRole string
DTLSRole is who drives the DTLS handshake. RFC 5763 §5.
const ( // DTLSRoleActive: this side will send ClientHello (initiator). DTLSRoleActive DTLSRole = "active" // DTLSRolePassive: this side will wait for ClientHello. DTLSRolePassive DTLSRole = "passive" // DTLSRoleActPass: offerer's "no preference"; answerer must // pick active or passive. DTLSRoleActPass DTLSRole = "actpass" // DTLSRoleHoldConn: keep an existing DTLS conn (RFC 5763 §5.3). // We never offer this; receiving it from a peer means "don't // renegotiate", we treat it as opaque. DTLSRoleHoldConn DTLSRole = "holdconn" )
func AnswerRole ¶
AnswerRole picks our role on the answer side given what the offerer proposed. RFC 5763 §5: an offer of `actpass` lets us choose; `active` from the offerer forces us passive; `passive` from the offerer forces us active; `holdconn` means "no DTLS renegotiation requested" — we don't touch DTLS state.
Returns DTLSRoleActPass when the offer was missing or unrecognised (caller should treat that as "no DTLS-SRTP requested").
type Fingerprint ¶
Fingerprint is one RFC 8122 cert fingerprint declaration. HashFunc is the IANA hash registry name (lowercase: "sha-256", "sha-384", "sha-512"). Hex is the colon-separated uppercase hex digest exactly as it appears on the wire.
func ParseFingerprint ¶
func ParseFingerprint(s string) Fingerprint
ParseFingerprint parses the value of one `a=fingerprint:` line (without the prefix). Returns zero Fingerprint when malformed.
type Info ¶
type Info struct {
IP string
Port int
Proto string
Codecs []Codec
// CryptoOffers lists a=crypto attributes from the first m=audio section (RFC 4568 SDES), if present.
CryptoOffers []CryptoOffer
// Fingerprints lists a=fingerprint values from the first m=audio
// section (RFC 8122). Multiple entries are allowed when the peer
// has both an EC and an RSA cert configured; we accept any. Empty
// when DTLS-SRTP isn't negotiated.
Fingerprints []Fingerprint
// DTLSRole is the parsed a=setup value from the first m=audio
// section (RFC 5763 §5). DTLSRoleActPass is the default when the
// attribute is absent, per RFC 5763 §5.
DTLSRole DTLSRole
}
Info holds minimal audio media information extracted from an SDP body.