sdp

package
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Jun 8, 2026 License: MIT Imports: 10 Imported by: 0

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

View Source
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"
)
View Source
const DefaultSessionName = "SoulNexus SIP"

DefaultSessionName is the fixed s= line used by Generate / GenerateWithProto.

Variables

This section is empty.

Functions

func DecodeSDESInline

func DecodeSDESInline(keyParams string) (masterKey, masterSalt []byte, err error)

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

func FormatCryptoLine(tag uint32, suite string, masterKey, masterSalt []byte) (string, error)

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

func FormatSetupLine(role DTLSRole) string

FormatSetupLine renders a complete `a=setup:` SDP attribute line. Returns "" when role is invalid.

func Generate

func Generate(localIP string, localPort int, codecs []Codec) string

Generate builds a minimal audio SDP offer/answer (m= uses proto RTP/AVP).

func GenerateWithProto

func GenerateWithProto(localIP string, localPort int, proto string, codecs []Codec) string

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

func IsDTLSTransport(proto string) bool

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

func NormalizeBody(body string) string

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

type Codec struct {
	PayloadType uint8
	Name        string
	ClockRate   int
	Channels    int
}

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

func PickTelephoneEventFromOffer(offer []Codec, matchClockRate int) (Codec, bool)

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:

  1. AES_CM_128_HMAC_SHA1_80 — interop default, WebRTC-compatible
  2. 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

func AnswerRole(offerRole DTLSRole) DTLSRole

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").

func ParseRole

func ParseRole(s string) DTLSRole

ParseRole parses an `a=setup:` value. Returns DTLSRoleActPass (the safe default per RFC 5763) when input is malformed.

func (DTLSRole) IsValid

func (r DTLSRole) IsValid() bool

IsValid reports whether r is one of the four RFC 5763 values.

type Fingerprint

type Fingerprint struct {
	HashFunc string
	Hex      string
}

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.

func Parse

func Parse(body string) (*Info, error)

Parse extracts connection IP, audio m= port/proto, and codec list from an SDP body.

Jump to

Keyboard shortcuts

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