Documentation
¶
Index ¶
- Constants
- Variables
- func AMRNBModeSet(fmtp string) []int
- func AMRNBOctetAligned(fmtp string) bool
- func AMRWBModeSet(fmtp string) []int
- func AMRWBOctetAligned(fmtp string) bool
- func AddressFamily(ip string) string
- func BuildMultipartMixed(boundary string, parts []BodyPart) (string, []byte, error)
- func CanonicalizeAOR(u sip.Uri) string
- func ClampAMRNBMode(ceiling int, modeSet []int) int
- func ClampAMRWBMode(ceiling int, modeSet []int) int
- func DTMFDigitToEvent(digit rune) (uint8, bool)
- func DTMFEventToDigit(event uint8) (rune, bool)
- func EncodeDTMFEvent(ev DTMFEvent) []byte
- func FormatAMRNBModeSet(modes []int) string
- func FormatAMRWBModeSet(modes []int) string
- func FormatSessionExpires(interval uint32, refresher string) string
- func GenerateAnswer(cfg SDPConfig, selected codec.CodecType, selectedPT uint8, textRejected bool) []byte
- func GenerateDTMFPackets(digit rune, pt uint8, ssrc uint32, baseSeq uint16, baseTS uint32, ...) []*rtp.Packet
- func GenerateOffer(cfg SDPConfig) []byte
- func GenerateReInviteSDP(cfg SDPConfig, selected codec.CodecType, selectedPT uint8, direction string) []byte
- func HasOptionTag(msg BodyCarrier, tag string) bool
- func HasSRCFeatureTag(req *sip.Request) bool
- func HoldDirection(desired string, held bool) string
- func IsSIPRECInvite(call *InboundCall) bool
- func IsWhatsAppInvite(call *InboundCall) bool
- func JoinHostPort(host string, port int) string
- func MirrorDirection(offered string) string
- func MultipartBoundary(seed string) string
- func NarrowDirection(mirrored, max string) string
- func NegotiateCodec(remote *SDPMedia, supported []codec.CodecType) (codec.CodecType, uint8, bool)
- func NegotiateCodecPreferred(remote *SDPMedia, supported []codec.CodecType, preferred codec.CodecType) (codec.CodecType, uint8, bool)
- func NegotiateCodecStream(remote *RemoteAudioStream, supported []codec.CodecType, ...) (codec.CodecType, uint8, bool)
- func OptionTags(msg BodyCarrier, header string) []string
- func ParseMinSE(value string) uint32
- func ParseSessionExpires(value string) (interval uint32, refresher string)
- func ParseSipfrag(body []byte) (int, string)
- func SDPOf(msg BodyCarrier) ([]byte, error)
- func TelephoneEventClockRate(c codec.CodecType) int
- func UDPNetwork(listenIP string) string
- func UnsupportedHeader(tags ...string) sip.Header
- func WhatsAppRecipientURI(toUser string) sip.Uri
- type AnswerOptions
- type AudioStream
- type AuthResult
- type Binding
- type BodyCarrier
- type BodyPart
- type ChallengeParams
- type DTMFEvent
- type Engine
- func (e *Engine) AMRNBMode() int
- func (e *Engine) AMRNBOctetAligned() bool
- func (e *Engine) AMRWBMode() int
- func (e *Engine) AMRWBOctetAligned() bool
- func (e *Engine) AdvertisedIPForFamily(family string) string
- func (e *Engine) AllowHeader() sip.Header
- func (e *Engine) BindIP() string
- func (e *Engine) BindIPV6() string
- func (e *Engine) ChallengeInvite(call *InboundCall, p ChallengeParams) error
- func (e *Engine) Codecs() []codec.CodecType
- func (e *Engine) ContactForInvite(req *sip.Request) *sip.ContactHeader
- func (e *Engine) DestinationsPinned() uint64
- func (e *Engine) DialogRespond(d *sipgo.DialogServerSession, statusCode int, reason string, body []byte, ...) error
- func (e *Engine) Invite(ctx context.Context, recipient sip.Uri, opts InviteOptions) (*OutboundCall, error)
- func (e *Engine) InviteWhatsApp(ctx context.Context, recipient sip.Uri, opts WhatsAppInviteOptions) (*WhatsAppOutboundCall, error)
- func (e *Engine) LogSyntheticResponse(req *sip.Request, statusCode int, reason string, body []byte, ...)
- func (e *Engine) OnInvite(handler func(*InboundCall))
- func (e *Engine) OnNotify(handler func(callID string, statusCode int, reason string, terminated bool))
- func (e *Engine) OnReInvite(handler func(callID string, body *MessageBody) []byte)
- func (e *Engine) OnRefer(...)
- func (e *Engine) OnRegisterAttempt(handler func(*RegisterAttempt) RegisterDecision)
- func (e *Engine) OnUpdate(handler func(callID string, body *MessageBody, hasSDP bool) []byte)
- func (e *Engine) PortAllocator() *PortAllocator
- func (e *Engine) PublicHost() string
- func (e *Engine) Registrar() *Registrar
- func (e *Engine) RespondFromSource(tx sip.ServerTransaction, req *sip.Request, statusCode int, reason string) error
- func (e *Engine) RespondInviteSDP(dialog *sipgo.DialogServerSession, sdp []byte) error
- func (e *Engine) SIPDebug() bool
- func (e *Engine) SIPHost() string
- func (e *Engine) SendNotifySipfrag(ctx context.Context, dialog interface{}, statusCode int, reason string, ...) error
- func (e *Engine) SendReInvite(ctx context.Context, dialog interface{}, sdpBody []byte) error
- func (e *Engine) SendReInviteAnswer(ctx context.Context, dialog interface{}, sdpBody []byte, ...) ([]byte, error)
- func (e *Engine) SendReInviteBody(ctx context.Context, dialog interface{}, sdpBody []byte, extra []BodyPart, ...) ([]byte, error)
- func (e *Engine) SendRefer(ctx context.Context, dialog interface{}, referTo string, ...) error
- func (e *Engine) Serve(ctx context.Context) error
- func (e *Engine) ServerHeader() sip.Header
- func (e *Engine) StrictMLineAnswer() bool
- func (e *Engine) TLSPort() int
- func (e *Engine) Trunks() *TrunkManager
- func (e *Engine) UserAgentHeader() sip.Header
- func (e *Engine) VerifyInboundAuth(req *sip.Request, method string) (AuthResult, string, int)
- type EngineConfig
- type ForkTarget
- type IPIPTrunkView
- type InboundCall
- type InviteOptions
- type MLineSlot
- type MLineTable
- func (t *MLineTable) ActiveAudio() []*MLineSlot
- func (t *MLineTable) ActiveAudioCount() int
- func (t *MLineTable) Append(s MLineSlot) int
- func (t *MLineTable) ByMID(mid string) (*MLineSlot, bool)
- func (t *MLineTable) ByStreamID(id string) (*MLineSlot, bool)
- func (t *MLineTable) Len() int
- func (t *MLineTable) LocalStreams(held bool, skipMedia map[int]bool) []AudioStream
- func (t *MLineTable) MintMID() string
- func (t *MLineTable) ReserveMID(mid string)
- func (t *MLineTable) Slot(i int) *MLineSlot
- func (t *MLineTable) Slots() []MLineSlot
- func (t *MLineTable) Tombstone(i int)
- type MessageBody
- type OfferStream
- type OutboundCall
- type OutboundRegistration
- func (r *OutboundRegistration) AOR() string
- func (r *OutboundRegistration) AppID() string
- func (r *OutboundRegistration) Credentials() (string, string)
- func (r *OutboundRegistration) FromHost() string
- func (r *OutboundRegistration) ID() string
- func (r *OutboundRegistration) PeerSocket() (host string, port int, transport string)
- func (r *OutboundRegistration) RegistrarURI() sip.Uri
- func (r *OutboundRegistration) Snapshot() TrunkView
- func (r *OutboundRegistration) Start(ctx context.Context)
- func (r *OutboundRegistration) Stop(ctx context.Context) error
- func (r *OutboundRegistration) Type() TrunkType
- type OutboundRegistrationConfig
- type OutboundRegistrationParams
- type PortAllocator
- type RTPSession
- func (s *RTPSession) Close() error
- func (s *RTPSession) LocalPort() int
- func (s *RTPSession) ReadRTP() (*rtp.Packet, error)
- func (s *RTPSession) RemoteAddr() *net.UDPAddr
- func (s *RTPSession) SendKeepalive(payloadType uint8, count int)
- func (s *RTPSession) SetReadDeadline(t time.Time) error
- func (s *RTPSession) SetRemote(ip string, port int) error
- func (s *RTPSession) WriteRTP(pkt *rtp.Packet) error
- type RegisterAttempt
- type RegisterDecision
- type RegisterDecisionKind
- type Registrar
- func (r *Registrar) Bind(b Binding)
- func (r *Registrar) ClampExpires(requested int) int
- func (r *Registrar) Config() RegistrarConfig
- func (r *Registrar) List() []Binding
- func (r *Registrar) Lookup(aor string) (Binding, bool)
- func (r *Registrar) LookupAll(aor string) []Binding
- func (r *Registrar) Start(ctx context.Context)
- func (r *Registrar) UnbindAll(aor, reason string) int
- func (r *Registrar) UnbindContact(aor, contactURI, reason string) bool
- type RegistrarConfig
- type RemoteAudioStream
- type RemoteMLine
- type ReplacesParams
- type SDPConfig
- type SDPMedia
- type SDPTextMedia
- type SIPRECSignals
- type SIPRegisterTrunkView
- type SessionTimerParams
- type SlotAction
- type SlotPlan
- type SlotState
- type Trunk
- type TrunkManager
- func (m *TrunkManager) Add(t Trunk)
- func (m *TrunkManager) Get(id string) Trunk
- func (m *TrunkManager) List() []Trunk
- func (m *TrunkManager) LookupByAORUser(user string) Trunk
- func (m *TrunkManager) LookupByFromAOR(aor string) Trunk
- func (m *TrunkManager) LookupByPeerSocket(host string, port int) Trunk
- func (m *TrunkManager) RefreshIndex(id string)
- func (m *TrunkManager) Remove(id string) Trunk
- func (m *TrunkManager) Shutdown(ctx context.Context)
- type TrunkStatus
- type TrunkType
- type TrunkView
- type WhatsAppInviteOptions
- type WhatsAppOutboundCall
Constants ¶
const ( ContentTypeSDP = "application/sdp" ContentTypeRSMetadata = "application/rs-metadata+xml" ContentTypeRSMetadataLegacy = "application/rs-metadata" )
SIP body content types we recognize.
Both SIPREC metadata types are accepted because the two RFCs contradicted each other: RFC 7865 specified application/rs-metadata+xml, RFC 7866 specified application/rs-metadata, and neither registered the type. RFC 9806 resolves that erratum (Err7987) in favour of +xml and registers it, but it also records the interoperability consequence -- an SRC written to RFC 7866 sends the unsuffixed name, which was normative for nine years.
const ( // DefaultMinSE is the minimum session interval we accept (RFC 4028 §4). DefaultMinSE = 90 // DefaultSessionExpires is the default session interval when the remote // requests timers but doesn't specify an interval. DefaultSessionExpires = 1800 )
const ( DirSendRecv = "sendrecv" DirSendOnly = "sendonly" DirRecvOnly = "recvonly" DirInactive = "inactive" )
Media direction attribute values (RFC 4566 / RFC 8866).
const OptionTagSIPREC = "siprec"
OptionTagSIPREC is the option tag an SRC puts in Require to demand a recording session (RFC 7866 §6.1).
const (
WhatsAppOutboundHost = "wa.meta.vc"
)
Variables ¶
var ErrNotRTP = errors.New("not an RTP packet")
ErrNotRTP is returned by ReadRTP when a received UDP packet is not valid RTP (e.g. RTCP, STUN). Callers should continue reading on this error.
Functions ¶
func AMRNBModeSet ¶ added in v0.9.0
AMRNBModeSet parses an AMR-NB "mode-set=a,b,c" fmtp parameter into the set of allowed speech modes (0..7). Returns nil when no valid mode-set is present (RFC 4867: absence means all modes are permitted). Modes outside 0..7 are dropped.
func AMRNBOctetAligned ¶ added in v0.9.0
AMRNBOctetAligned reports whether the AMR-NB fmtp params select octet-aligned framing. Per RFC 4867 the default (no octet-align) is bandwidth-efficient, so an absent or "octet-align=0" parameter means bandwidth-efficient.
func AMRWBModeSet ¶ added in v0.9.0
AMRWBModeSet parses an AMR-WB "mode-set=a,b,c" fmtp parameter into the set of allowed speech modes (0..8). Returns nil when no valid mode-set is present (RFC 4867: absence means all modes are permitted). Modes outside 0..8 are dropped.
func AMRWBOctetAligned ¶ added in v0.9.0
AMRWBOctetAligned reports whether the AMR-WB fmtp params select octet-aligned framing. Per RFC 4867 the default (no octet-align) is bandwidth-efficient, so an absent or "octet-align=0" parameter means bandwidth-efficient.
func AddressFamily ¶ added in v0.6.0
AddressFamily returns the SDP address-type token ("IP4" or "IP6") for an IP literal, or "" when the input is empty / not a literal IP.
func BuildMultipartMixed ¶ added in v0.12.0
BuildMultipartMixed renders parts as a multipart/mixed body, returning the Content-Type header value to send with it. The boundary is supplied by the caller so the output is deterministic.
func CanonicalizeAOR ¶ added in v0.8.0
CanonicalizeAOR returns the registry key for an AOR URI. The transformation is: lower-case scheme, strip user-info parameters, lower-case host, keep port only when explicit, drop URI params. Returns "" for malformed input.
func ClampAMRNBMode ¶ added in v0.9.0
ClampAMRNBMode constrains a desired ceiling mode to the peer's negotiated mode-set. Behaviour mirrors ClampAMRWBMode but ranges over AMR-NB modes 0..7.
func ClampAMRWBMode ¶ added in v0.9.0
ClampAMRWBMode constrains a desired ceiling mode to the peer's negotiated mode-set: it returns the highest set member <= ceiling, or — when the ceiling is below every member — the lowest member (so the result always stays inside the set). A nil/empty set means no restriction, so the ceiling is returned.
func DTMFDigitToEvent ¶
DTMFDigitToEvent converts a DTMF digit character to its RFC 4733 event code.
func DTMFEventToDigit ¶
DTMFEventToDigit converts an RFC 4733 event code to a digit character.
func EncodeDTMFEvent ¶
EncodeDTMFEvent encodes a DTMFEvent into a 4-byte RFC 4733 payload.
func FormatAMRNBModeSet ¶ added in v0.9.0
FormatAMRNBModeSet renders modes as a "0,4,7" mode-set value (the inverse of AMRNBModeSet); returns "" for an empty set.
func FormatAMRWBModeSet ¶ added in v0.9.0
FormatAMRWBModeSet renders modes as a "0,1,2" mode-set value (the inverse of AMRWBModeSet); returns "" for an empty set.
func FormatSessionExpires ¶
FormatSessionExpires formats a Session-Expires header value.
func GenerateAnswer ¶
func GenerateAnswer(cfg SDPConfig, selected codec.CodecType, selectedPT uint8, textRejected bool) []byte
GenerateAnswer builds an SDP answer with a single selected codec. selectedPT echoes the remote offer's PT for dynamic codecs. When cfg.TextRTPPort != 0 the answer accepts RTT; when textRejected is true the answer includes a port=0 m=text section per RFC 3264.
func GenerateDTMFPackets ¶
func GenerateDTMFPackets(digit rune, pt uint8, ssrc uint32, baseSeq uint16, baseTS uint32, samplesPerPkt uint16) []*rtp.Packet
GenerateDTMFPackets builds the sequence of RTP packets for one DTMF digit. Returns 7 packets: 4 normal (80ms) + 3 end-of-event, each 20ms apart. The timestamp remains fixed for the entire event per RFC 4733. samplesPerPkt is the telephone-event clock rate per 20ms (e.g. 160 at 8kHz, 320 at AMR-WB's 16kHz); it sets the units of the encoded event duration and must match the negotiated telephone-event clock rate or strict peers drop the digit as too short.
func GenerateOffer ¶
GenerateOffer builds an SDP offer with all configured codecs.
func GenerateReInviteSDP ¶
func GenerateReInviteSDP(cfg SDPConfig, selected codec.CodecType, selectedPT uint8, direction string) []byte
GenerateReInviteSDP builds an SDP body for a re-INVITE (hold/unhold). It is similar to GenerateAnswer but uses the specified direction attribute.
func HasOptionTag ¶ added in v0.12.0
func HasOptionTag(msg BodyCarrier, tag string) bool
HasOptionTag reports whether Require or Proxy-Require demands tag.
func HasSRCFeatureTag ¶ added in v0.12.0
HasSRCFeatureTag reports whether the Contact header identifies a session recording client.
func HoldDirection ¶ added in v0.12.0
HoldDirection returns the direction to advertise for a stream whose intent is desired while the leg is on hold. Off hold the intent passes through.
func IsSIPRECInvite ¶ added in v0.12.0
func IsSIPRECInvite(call *InboundCall) bool
IsSIPRECInvite reports whether an inbound INVITE opens a recording session.
func IsWhatsAppInvite ¶ added in v0.5.0
func IsWhatsAppInvite(call *InboundCall) bool
IsWhatsAppInvite reports whether an inbound INVITE comes from a Meta WhatsApp gateway (meta.vc or any subdomain).
func JoinHostPort ¶ added in v0.6.0
JoinHostPort wraps net.JoinHostPort with an int port — bracket-safe for IPv6 literals and a no-op for IPv4 / hostnames.
func MirrorDirection ¶ added in v0.12.0
MirrorDirection returns the direction an answer must carry for an offered direction, per RFC 3264 §6.1. An offered sendrecv permits any answer; we mirror it as sendrecv and let callers narrow it further.
func MultipartBoundary ¶ added in v0.12.0
MultipartBoundary derives a stable boundary token from seed (typically the Call-ID), so retransmissions and golden tests reproduce the same bytes.
func NarrowDirection ¶ added in v0.12.0
NarrowDirection restricts a mirrored answer direction to at most max, which RFC 3264 §6.1 permits: an answer to sendrecv may be recvonly. An empty max leaves the mirrored direction alone. A recording session uses this to stay receive-only even when the offer was sendrecv.
func NegotiateCodec ¶
NegotiateCodec finds the first codec in the remote SDP that is also in the supported list. Returns the codec type, the payload type from the remote SDP, and whether negotiation succeeded.
func NegotiateCodecPreferred ¶ added in v0.6.0
func NegotiateCodecPreferred(remote *SDPMedia, supported []codec.CodecType, preferred codec.CodecType) (codec.CodecType, uint8, bool)
NegotiateCodecPreferred is like NegotiateCodec but biases the choice toward preferred when it is non-zero. The preferred codec must appear in both the remote offer and the supported list; otherwise selection falls back to the regular preference order.
func NegotiateCodecStream ¶ added in v0.12.0
func NegotiateCodecStream(remote *RemoteAudioStream, supported []codec.CodecType, preferred codec.CodecType) (codec.CodecType, uint8, bool)
NegotiateCodecStream is NegotiateCodecPreferred for one parsed m=audio section, so each stream of a multi-stream offer negotiates independently.
func OptionTags ¶ added in v0.12.0
func OptionTags(msg BodyCarrier, header string) []string
OptionTags returns the option tags carried by every instance of a header, lowercased and trimmed.
func ParseMinSE ¶
ParseMinSE parses a Min-SE header value, e.g. "90" → 90.
func ParseSessionExpires ¶
ParseSessionExpires parses a Session-Expires header value, e.g. "1800;refresher=uac" → interval=1800, refresher="uac".
func ParseSipfrag ¶ added in v0.3.0
ParseSipfrag returns (statusCode, reason) from a sipfrag status line, or (0,"").
func SDPOf ¶ added in v0.12.0
func SDPOf(msg BodyCarrier) ([]byte, error)
SDPOf extracts the session description from a SIP message, transparently unwrapping a multipart body.
func TelephoneEventClockRate ¶ added in v0.9.0
TelephoneEventClockRate returns the RTP clock rate to pair with the telephone-event (RFC 4733) format for codec c. RFC 4733 requires the telephone-event clock rate to match the audio codec's RTP clock rate, so AMR-WB uses 16 kHz; all other codecs use the conventional 8 kHz.
func UDPNetwork ¶ added in v0.6.0
UDPNetwork returns the UDP network string ("udp", "udp4", or "udp6") to use for a given listen IP literal. An empty string or "::" yields "udp" so the OS can give us a dual-stack socket on Linux when bindv6only=0.
func UnsupportedHeader ¶ added in v0.12.0
UnsupportedHeader builds the Unsupported header that must accompany a 420 Bad Extension (RFC 3261 §8.2.2.3).
func WhatsAppRecipientURI ¶ added in v0.5.0
WhatsAppRecipientURI builds the Request-URI for an outbound call. Meta uses "sip:+E164@wa.meta.vc;transport=tls" — using "sips:" returns 404 because their internal routing isn't strict-TLS end-to-end.
Types ¶
type AnswerOptions ¶ added in v0.12.0
type AnswerOptions struct {
SupportedCodecs []codec.CodecType
// Preferred biases codec selection for the first accepted audio section.
Preferred codec.CodecType
// StrictMLines makes the plan cover every offered m= section, rejecting the
// ones we don't handle with port 0 as RFC 3264 §6 requires. With it off
// the plan omits unhandled non-audio sections, preserving the legacy
// (spec-violating) answer shape.
StrictMLines bool
// TextMLineIndex is the m= section the caller renders itself as m=text, or
// -1 when there is none.
TextMLineIndex int
// MaxDirection caps every accepted section's answer direction. Empty means
// no cap; DirRecvOnly makes the answer receive-only whatever was offered,
// which is what a recording session (RFC 7866) needs.
MaxDirection string
}
AnswerOptions parameterizes PlanAnswer.
type AudioStream ¶ added in v0.12.0
type AudioStream struct {
// MID (RFC 5888), Label (RFC 4574), Content (RFC 4796) and Lang (RFC 8866,
// a BCP 47 tag) are each omitted when empty.
MID string
Label string
Content string
Lang string
Port int
Direction string // "" means sendrecv
Ptime int // 0 means 20
// Codecs holds every codec for an offer, or the single selected codec for
// an answer. CodecPTs overrides the default payload type per codec —
// answers must echo the offerer's dynamic PTs.
Codecs []codec.CodecType
CodecPTs map[codec.CodecType]uint8
DTMFPT uint8
DTMFClockRate int
OfferTE48k bool // emit the extra telephone-event/48000 line used with Opus
AMRWBOctetAligned bool
AMRWBModeSet string
AMRNBOctetAligned bool
AMRNBModeSet string
}
AudioStream describes one m=audio section to generate. A zero Port emits a rejected/disabled section per RFC 3264 §6.
func (*AudioStream) PayloadType ¶ added in v0.12.0
func (s *AudioStream) PayloadType(c codec.CodecType) uint8
PayloadType returns the payload type to advertise for c, preferring an explicit CodecPTs entry over the codec's static default.
type AuthResult ¶ added in v0.10.0
type AuthResult int
AuthResult is the outcome of verifying an Authorization header against a previously issued challenge.
const ( // AuthNone means no Authorization header was present, or no live pending // challenge matched it. The caller should surface the request as // unauthenticated and let the client decide whether to challenge. AuthNone AuthResult = iota // AuthValid means the digest response matched the expected credential. AuthValid // AuthInvalid means a matching challenge was found but the response (or // username) did not verify. The caller should reject with 403. AuthInvalid )
type Binding ¶ added in v0.8.0
type Binding struct {
AOR string
Contact string
Socket string
Transport string
UserAgent string
CallID string
AppID string
CreatedAt time.Time
LastRefresh time.Time
ExpiresAt time.Time
GrantedExpires int // seconds granted at bind time (constant for the binding)
}
Binding represents one Contact registered under an AOR.
type BodyCarrier ¶ added in v0.12.0
BodyCarrier is the subset of a SIP message that carries a body. Both *sip.Request and *sip.Response satisfy it.
type BodyPart ¶ added in v0.12.0
type BodyPart struct {
// ContentType is lowercased with parameters stripped, e.g. "application/sdp".
ContentType string
Params map[string]string
// Disposition is the Content-Disposition type ("session",
// "recording-session"), lowercased and without parameters.
Disposition string
Headers map[string]string
Data []byte
}
BodyPart is one part of a SIP message body. A non-multipart body is represented as a single part holding the whole body.
type ChallengeParams ¶ added in v0.10.0
type ChallengeParams struct {
Realm string // digest realm advertised in the challenge
Username string // expected username; empty accepts whatever username the response carries
Password string // plaintext secret (mutually exclusive with HA1)
HA1 string // precomputed H(username:realm:password) hex (mutually exclusive with Password)
Algorithm string // "MD5" (default), "SHA-256", "SHA-512-256"
QOP []string // advertised qop, e.g. ["auth"]; empty omits the qop directive
}
ChallengeParams carries the digest-challenge inputs a VSI/REST client supplies when it decides to challenge an inbound INVITE or REGISTER. The credential (Password or HA1) is held only for the lifetime of the pending challenge and is never persisted. Exactly one of Password or HA1 must be set.
type DTMFEvent ¶
type DTMFEvent struct {
Event uint8 // 0-9, 10=*, 11=#, 12-15=A-D
EndOfEvent bool
Volume uint8 // 0-63
Duration uint16 // in timestamp units
}
DTMFEvent represents an RFC 4733 telephone-event payload.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine wraps sipgo server/client + dialog caches for SIP signaling.
func NewEngine ¶
func NewEngine(cfg EngineConfig) (*Engine, error)
NewEngine creates a SIP engine with the given configuration.
func (*Engine) AMRNBMode ¶ added in v0.9.0
AMRNBMode returns the configured AMR-NB encoder speech mode (0..7).
func (*Engine) AMRNBOctetAligned ¶ added in v0.9.0
AMRNBOctetAligned reports whether AMR-NB offers advertise octet-aligned framing.
func (*Engine) AMRWBMode ¶ added in v0.9.0
AMRWBMode returns the configured AMR-WB encoder speech mode (0..8).
func (*Engine) AMRWBOctetAligned ¶ added in v0.9.0
AMRWBOctetAligned reports whether AMR-WB offers advertise octet-aligned framing.
func (*Engine) AdvertisedIPForFamily ¶ added in v0.6.0
AdvertisedIPForFamily returns the configured advertised IP for the given SDP address family token ("IP4" or "IP6"). Falls back to the other family when the requested one is unconfigured. Returns empty when neither is configured (caller should reject the call).
func (*Engine) AllowHeader ¶ added in v0.9.0
AllowHeader returns an Allow header listing every SIP method this UA answers (RFC 3261 §20.5). The list comes from sipgo's registered request handlers so it can never drift from the methods we actually accept.
func (*Engine) BindIP ¶
BindIP returns the engine's IPv4 advertised address. May be empty in IPv6-only deployments.
func (*Engine) BindIPV6 ¶ added in v0.6.0
BindIPV6 returns the engine's IPv6 advertised address. Empty when no v6 is configured.
func (*Engine) ChallengeInvite ¶ added in v0.10.0
func (e *Engine) ChallengeInvite(call *InboundCall, p ChallengeParams) error
ChallengeInvite sends a 401 Unauthorized carrying a digest WWW-Authenticate challenge on an unanswered inbound INVITE and records the pending challenge so the credentialed re-INVITE (same Call-ID) can be verified by VerifyInboundAuth.
func (*Engine) ContactForInvite ¶ added in v0.6.0
func (e *Engine) ContactForInvite(req *sip.Request) *sip.ContactHeader
ContactForInvite is the public form of contactForInvite, used by callers outside this package that need to attach a transport-appropriate Contact header to a dialog response.
func (*Engine) DestinationsPinned ¶ added in v0.6.0
DestinationsPinned returns the cumulative count of responses whose destination has been overridden via SIP_USE_SOURCE_SOCKET. Tests use this to assert the flag actually fired rather than silently no-op'd.
func (*Engine) DialogRespond ¶ added in v0.6.0
func (e *Engine) DialogRespond(d *sipgo.DialogServerSession, statusCode int, reason string, body []byte, headers ...sip.Header) error
DialogRespond sends a response on a UAS dialog, pinning the destination to the original INVITE source when SIP_USE_SOURCE_SOCKET is enabled. Replaces direct calls to dialog.Respond / dialog.RespondSDP — those reach tx.Respond internally without giving us a hook to override Destination.
func (*Engine) Invite ¶
func (e *Engine) Invite(ctx context.Context, recipient sip.Uri, opts InviteOptions) (*OutboundCall, error)
Invite sends an outbound INVITE and returns an OutboundCall on success. When opts.ForkTargets has more than one entry the INVITE is parallel-forked (RFC 3261 §16); the first branch to reach a 2xx response wins and the others are CANCELled.
func (*Engine) InviteWhatsApp ¶ added in v0.5.0
func (e *Engine) InviteWhatsApp(ctx context.Context, recipient sip.Uri, opts WhatsAppInviteOptions) (*WhatsAppOutboundCall, error)
InviteWhatsApp sends an outbound INVITE over SIP/TLS with a pre-built WebRTC SDP offer and digest auth credentials.
func (*Engine) LogSyntheticResponse ¶ added in v0.5.0
func (e *Engine) LogSyntheticResponse(req *sip.Request, statusCode int, reason string, body []byte, headers ...sip.Header)
LogSyntheticResponse constructs a response from a request (mirroring what sipgo would build internally) purely for SIP_DEBUG logging. The actual response still goes out through dialog.Respond / dialog.RespondSDP; this is a best-effort wire-form dump so the body and headers we ask sipgo to include are visible on the debug channel.
func (*Engine) OnInvite ¶
func (e *Engine) OnInvite(handler func(*InboundCall))
OnInvite registers a handler for inbound INVITE requests.
func (*Engine) OnNotify ¶ added in v0.3.0
func (e *Engine) OnNotify(handler func(callID string, statusCode int, reason string, terminated bool))
OnNotify registers a handler for in-dialog NOTIFY requests carrying a "refer" subscription (RFC 3515 sipfrag). It is invoked once per NOTIFY with the subscription's terminal/transient SIP status parsed from the sipfrag body.
func (*Engine) OnReInvite ¶
func (e *Engine) OnReInvite(handler func(callID string, body *MessageBody) []byte)
OnReInvite registers a handler for in-dialog re-INVITE requests (hold/unhold). The handler receives the SIP Call-ID and the SDP direction attribute, and returns the SDP body to include in the 200 OK response (nil = no SDP).
func (*Engine) OnRefer ¶ added in v0.3.0
func (e *Engine) OnRefer(handler func(callID string, target string, replaces *ReplacesParams, req *sip.Request, tx sip.ServerTransaction))
OnRefer registers a handler for in-dialog REFER requests (transfer). The handler is responsible for sending the SIP response (typically 202 Accepted, or 603 Decline when transfers are disabled). req is provided so the handler can pass it to sip.NewResponseFromRequest.
func (*Engine) OnRegisterAttempt ¶ added in v0.10.0
func (e *Engine) OnRegisterAttempt(handler func(*RegisterAttempt) RegisterDecision)
OnRegisterAttempt registers the decision callback consulted for each inbound REGISTER that would create or remove a binding. The handler returns whether to accept, challenge (401), or reject the REGISTER. When nil (handler not registered) every REGISTER is auto-accepted, preserving the unauthenticated default.
func (*Engine) OnUpdate ¶ added in v0.9.0
func (e *Engine) OnUpdate(handler func(callID string, body *MessageBody, hasSDP bool) []byte)
OnUpdate registers a handler for in-dialog UPDATE requests (RFC 3311), used for session-timer refresh (RFC 4028) and mid-dialog media changes. hasSDP indicates whether the UPDATE carried an SDP offer; when true, direction is the parsed a=sendrecv/sendonly/recvonly/inactive attribute and the returned []byte is the SDP answer for the 200 OK. When false, direction is "" and the handler should only refresh session-timer state.
func (*Engine) PortAllocator ¶
func (e *Engine) PortAllocator() *PortAllocator
PortAllocator returns the engine's port allocator (nil if OS-assigned).
func (*Engine) PublicHost ¶ added in v0.5.0
PublicHost returns the canonical signalling hostname (SIP_DOMAIN when set, otherwise the advertised IP).
func (*Engine) Registrar ¶ added in v0.8.0
Registrar returns the engine's AOR registry (may be nil when the engine was created without one).
func (*Engine) RespondFromSource ¶ added in v0.3.0
func (e *Engine) RespondFromSource(tx sip.ServerTransaction, req *sip.Request, statusCode int, reason string) error
RespondFromSource pins the response destination to the request's UDP source so peers with unroutable Via headers still get our reply.
func (*Engine) RespondInviteSDP ¶ added in v0.5.0
func (e *Engine) RespondInviteSDP(dialog *sipgo.DialogServerSession, sdp []byte) error
RespondInviteSDP sends a 2xx response to an inbound INVITE with a transport-appropriate Contact header. This is required for WhatsApp inbound calls, which arrive over TLS and need a sips: Contact pointing at our TLS port — otherwise the remote's ACK is routed to the wrong scheme/port, the dialog stays in Early state, and retransmits eventually kill the transaction.
func (*Engine) SIPDebug ¶ added in v0.5.0
SIPDebug reports whether SIP_DEBUG is enabled. Consumers that send responses via sipgo (dialog.Respond / RespondSDP) use this to gate LogSyntheticResponse calls.
func (*Engine) SendNotifySipfrag ¶ added in v0.3.0
func (e *Engine) SendNotifySipfrag(ctx context.Context, dialog interface{}, statusCode int, reason string, terminated bool) error
SendNotifySipfrag sends a "refer" subscription NOTIFY with a sipfrag body.
func (*Engine) SendReInvite ¶
SendReInvite sends a re-INVITE within an existing dialog for hold/unhold, discarding the peer's answer. Prefer SendReInviteAnswer when the answer matters — which it does for anything beyond a plain direction flip. dialog must be either *sipgo.DialogServerSession or *sipgo.DialogClientSession.
func (*Engine) SendReInviteAnswer ¶ added in v0.12.0
func (e *Engine) SendReInviteAnswer(ctx context.Context, dialog interface{}, sdpBody []byte, apply func(answer []byte)) ([]byte, error)
SendReInviteAnswer sends a re-INVITE and returns the peer's answer body from the 200 OK. When apply is non-nil it is invoked with that body *before* the ACK is written, so local media state is already consistent by the time the peer is cleared to send.
func (*Engine) SendReInviteBody ¶ added in v0.12.0
func (e *Engine) SendReInviteBody(ctx context.Context, dialog interface{}, sdpBody []byte, extra []BodyPart, apply func(answer []byte)) ([]byte, error)
SendReInviteBody is SendReInviteAnswer with extra body parts appended, so a re-INVITE can carry an updated SIPREC metadata document alongside the offer. With no extra parts the request is byte-identical to the plain SDP form.
func (*Engine) SendRefer ¶ added in v0.3.0
func (e *Engine) SendRefer(ctx context.Context, dialog interface{}, referTo string, replaces *ReplacesParams) error
SendRefer sends an in-dialog REFER, returning nil on 202 Accepted.
func (*Engine) Serve ¶
Serve starts the SIP server and blocks until ctx is cancelled. When TLSBindPort is configured it runs UDP and TLS listeners concurrently; if either fails the other is torn down via ctx cancellation. A secondary UDP listener is started when listenIPV6 is set to a literal distinct from listenIP (rare dual-bind case for hosts with bindv6only=1).
func (*Engine) ServerHeader ¶ added in v0.4.0
ServerHeader returns a SIP Server header for UAS responses.
func (*Engine) StrictMLineAnswer ¶ added in v0.12.0
StrictMLineAnswer reports whether answers cover every offered m= section.
func (*Engine) TLSPort ¶ added in v0.5.0
TLSPort returns the configured SIP TLS port (0 = disabled).
func (*Engine) Trunks ¶ added in v0.9.0
func (e *Engine) Trunks() *TrunkManager
Trunks returns the engine's outbound SIP trunk manager. Always non-nil.
func (*Engine) UserAgentHeader ¶ added in v0.9.0
UserAgentHeader returns a SIP User-Agent header for UAC requests. Mirrors the Server header so peers see the same identity in both directions.
func (*Engine) VerifyInboundAuth ¶ added in v0.10.0
VerifyInboundAuth validates the Authorization header of an inbound request against the challenge previously issued for its Call-ID. method is the SIP method ("INVITE" / "REGISTER") signed by the digest. On AuthValid the second return value is the authenticated username and the third is the TTL cap (seconds, 0 = none) recorded with the challenge — meaningful only for REGISTER. Non-valid results return "" and 0.
type EngineConfig ¶
type EngineConfig struct {
BindIP string // IPv4 advertised address for SDP c= line / Contact (when v4 is in use)
BindIPV6 string // IPv6 advertised address; empty = v6 not advertised
ListenIP string // IPv4 socket bind (default: same as BindIP). Special values: "0.0.0.0", "::" (dual-stack)
ListenIPV6 string // IPv6 socket bind (default: same as BindIPV6). Used when configured separately from ListenIP.
ExternalIP string // IPv4 public IP override for NAT/Docker (v6 has no equivalent — set BindIPV6 directly)
PublicHost string // FQDN advertised in From/Contact/Via signaling headers; falls back to ExternalIP/BindIP when empty
BindPort int
TLSBindPort int // 0 = TLS disabled
TLSCertPath string // CA-signed cert (fullchain.pem) — required when TLSBindPort > 0
TLSKeyPath string // private key (privkey.pem) — required when TLSBindPort > 0
SIPDebug bool // dump full SIP request/response bodies on the debug channel
SIPHost string
// UseSourceSocket forces SIP responses and in-dialog requests to be
// routed back to the request's source socket (req.Source()) instead of
// the peer's Contact URI / Via sent-by. Required when peers are behind
// NAT and advertise unroutable addresses.
UseSourceSocket bool
Codecs []codec.CodecType
// AMRWBMode is the AMR-WB encoder speech mode (0..8) offered/transmitted.
AMRWBMode int
// AMRWBOctetAligned selects octet-aligned (true) vs bandwidth-efficient
// (false) AMR-WB framing in generated offers.
AMRWBOctetAligned bool
// AMRNBMode is the AMR-NB encoder speech mode (0..7) offered/transmitted.
AMRNBMode int
// AMRNBOctetAligned selects octet-aligned (true) vs bandwidth-efficient
// (false) AMR-NB framing in generated offers.
AMRNBOctetAligned bool
Log *slog.Logger
PortAllocator *PortAllocator // nil = OS-assigned ports
// Registrar, when non-nil, enables inbound REGISTER handling and AOR
// resolution for outbound INVITEs.
Registrar *Registrar
// NonceTTL bounds the lifetime of an issued inbound-auth challenge nonce.
// Zero falls back to the store default (60s).
NonceTTL time.Duration
// StrictMLineAnswer makes answers carry a port-0 placeholder for every
// offered section we don't accept, per RFC 3264 §6.
StrictMLineAnswer bool
// TCPEnabled adds a TCP listener on BindPort alongside the UDP one. Needed
// for inbound SIPREC: sipgo refuses to send a request over 1300 bytes on
// UDP (RFC 3261 §18.1.1), and a recording INVITE always exceeds that.
TCPEnabled bool
}
EngineConfig holds configuration for the SIP engine.
type ForkTarget ¶ added in v0.8.0
ForkTarget identifies one branch of a parallel-forked INVITE: an explicit transport-layer destination (ip:port) that overrides the routing implied by the Request-URI's host. Used when dialing a registered AOR with one or more bound contacts; one ForkTarget per bound contact.
type IPIPTrunkView ¶ added in v0.9.0
type IPIPTrunkView struct {
PeerURI string `json:"peer_uri,omitempty"`
}
IPIPTrunkView is the placeholder shape for the unimplemented ip_ip type.
type InboundCall ¶
type InboundCall struct {
Dialog *sipgo.DialogServerSession
From string // caller URI user part
To string // callee URI user part
RemoteSDP *SDPMedia // parsed offer SDP
Request *sip.Request
// Body is the parsed INVITE body. Multipart offers (SIPREC) carry the
// rs-metadata document alongside the SDP.
Body *MessageBody
// Session timer (RFC 4028) — populated when remote requests timers.
SessionTimer *SessionTimerParams // nil when remote didn't request timers
}
InboundCall wraps a sipgo DialogServerSession with parsed SDP.
type InviteOptions ¶
type InviteOptions struct {
Codecs []codec.CodecType // Override engine codecs for this call; nil = use engine default
Headers []sip.Header // Extra SIP headers to include in the INVITE
FromUser string // Override the user part of the From header (caller ID)
FromHost string // Override the host part of the From/P-Asserted-Identity URI (e.g. a registered trunk's AOR realm); empty = engine publicHost
OnEarlyMedia func(remoteSDP *SDPMedia, rtpSess *RTPSession) // Called on first 183 with SDP
AuthUsername string // SIP digest auth username (optional)
AuthPassword string // SIP digest auth password (optional)
// ForkTargets, when non-empty, routes the INVITE to the listed transport
// addresses. A single target overrides the destination for a single
// outbound dialog; multiple targets parallel-fork the INVITE and race
// for the first 2xx (RFC 3261 §16).
ForkTargets []ForkTarget
// RouteURI, when set, adds a single loose-route header pointing at the
// given URI (typically a trunk's upstream proxy/registrar). The ";lr"
// param is appended if not already present.
RouteURI *sip.Uri
// RTT (T.140 / RFC 4103) parameters. RTTEnabled offers m=text alongside
// audio in the INVITE. RTTRedundancy controls the RFC 2198 RED depth
// (0 = plain T.140, no RED).
RTTEnabled bool
RTTRedundancy int
// Streams, when it holds more than one entry, offers that many m=audio
// sections. The first is the call's primary bidirectional audio; the rest
// are additional streams (e.g. a translated feed). Empty or single-entry
// offers emit exactly the SDP a single-stream call always has.
Streams []OfferStream
// BodyParts, when non-empty, sends the offer as a multipart/mixed body with
// the SDP as its first part and these appended — how RFC 7866 carries the
// rs-metadata document alongside the offer. Empty emits the plain
// application/sdp body every other call uses.
BodyParts []BodyPart
}
InviteOptions holds optional parameters for outbound INVITE.
type MLineSlot ¶ added in v0.12.0
type MLineSlot struct {
Index int
Media string // "audio", "text", or a kind we only hold a place for
Proto []string // echoed verbatim when the slot is rejected
MID string
Label string
Content string
Lang string
State SlotState
// StreamID links an audio slot to the media stream carrying it. Empty for
// tombstones and for slots we merely hold a position for.
StreamID string
Local AudioStream // what we advertise
Remote RemoteAudioStream // what the peer last told us
}
MLineSlot is one position in a dialog's m-line vector.
type MLineTable ¶ added in v0.12.0
type MLineTable struct {
// contains filtered or unexported fields
}
MLineTable is a dialog's m-line vector. Slots are append-only for the life of the dialog: RFC 3264 §8 requires that the m-line count never decreases and that the i-th line always describes the same stream. Removal is a tombstone, never a deletion.
Not safe for concurrent use — the owning leg serializes access.
func (*MLineTable) ActiveAudio ¶ added in v0.12.0
func (t *MLineTable) ActiveAudio() []*MLineSlot
ActiveAudio returns the audio slots currently carrying media, in m-line order.
func (*MLineTable) ActiveAudioCount ¶ added in v0.12.0
func (t *MLineTable) ActiveAudioCount() int
ActiveAudioCount returns how many audio slots are active or awaiting an answer — the figure a per-dialog stream cap applies to.
func (*MLineTable) Append ¶ added in v0.12.0
func (t *MLineTable) Append(s MLineSlot) int
Append adds a slot at the next free position and returns its index. The slot's Index is overwritten to match.
func (*MLineTable) ByMID ¶ added in v0.12.0
func (t *MLineTable) ByMID(mid string) (*MLineSlot, bool)
ByMID returns the slot carrying the given a=mid value.
func (*MLineTable) ByStreamID ¶ added in v0.12.0
func (t *MLineTable) ByStreamID(id string) (*MLineSlot, bool)
ByStreamID returns the slot bound to the given media stream.
func (*MLineTable) Len ¶ added in v0.12.0
func (t *MLineTable) Len() int
Len returns the number of slots, i.e. the m-line count every offer and answer on this dialog must carry.
func (*MLineTable) LocalStreams ¶ added in v0.12.0
func (t *MLineTable) LocalStreams(held bool, skipMedia map[int]bool) []AudioStream
LocalStreams renders the table as the audio sections of an offer or answer: every slot in index order, tombstones as port 0. held applies the RFC 3264 hold transform to each active slot's desired direction.
Non-audio slots are returned as zero-port audio placeholders only when they were never ours to describe; callers that own an m=text section render it themselves and pass skipMedia to exclude it here.
func (*MLineTable) MintMID ¶ added in v0.12.0
func (t *MLineTable) MintMID() string
MintMID returns an a=mid value not yet used in this table.
func (*MLineTable) ReserveMID ¶ added in v0.12.0
func (t *MLineTable) ReserveMID(mid string)
ReserveMID records mid as used so MintMID never collides with it. Call it for every mid adopted from a peer's offer.
func (*MLineTable) Slot ¶ added in v0.12.0
func (t *MLineTable) Slot(i int) *MLineSlot
Slot returns the slot at index i, or nil when i is out of range.
func (*MLineTable) Slots ¶ added in v0.12.0
func (t *MLineTable) Slots() []MLineSlot
Slots returns the backing slice for iteration. Callers must not append to it.
func (*MLineTable) Tombstone ¶ added in v0.12.0
func (t *MLineTable) Tombstone(i int)
Tombstone disables the slot at index i permanently, keeping its position.
type MessageBody ¶ added in v0.12.0
MessageBody is a parsed SIP message body.
func BodyOf ¶ added in v0.12.0
func BodyOf(msg BodyCarrier) (*MessageBody, error)
BodyOf parses the body of a SIP message using its Content-Type header.
func ParseMessageBody ¶ added in v0.12.0
func ParseMessageBody(contentType string, body []byte) (*MessageBody, error)
ParseMessageBody splits body according to contentType. A body that is not multipart is returned verbatim as a single part, so every existing single-part call site keeps identical bytes.
func (*MessageBody) Part ¶ added in v0.12.0
func (b *MessageBody) Part(contentType string) ([]byte, bool)
Part returns the body of the first part with the given content type.
func (*MessageBody) RSMetadata ¶ added in v0.12.0
func (b *MessageBody) RSMetadata() ([]byte, bool)
RSMetadata returns the SIPREC metadata document (RFC 7865), accepting both the registered and the legacy content type.
func (*MessageBody) SDP ¶ added in v0.12.0
func (b *MessageBody) SDP() ([]byte, bool)
SDP returns the session description. A single-part body is returned whatever its declared type, preserving the behaviour of peers that omit or misdeclare Content-Type on a plain SDP offer — except when it declares itself a type we know is not SDP.
RFC 7866 §9.1 lets an SRC send metadata with no SDP in an INVITE, an UPDATE, or a 200 to an offerless INVITE, and is explicit that "when a SIP message contains only an SDP offer or metadata, the multipart/mixed container is optional". So a bare rs-metadata body is a correct request, and taking it for an offer answers it 400 Bad SDP.
type OfferStream ¶ added in v0.12.0
type OfferStream struct {
Direction string // "" means sendrecv
Lang string // BCP 47 tag for a=lang
Content string // a=content, e.g. "main" or "alt"
Label string // a=label
}
OfferStream describes one m=audio section to offer beyond the defaults.
type OutboundCall ¶
type OutboundCall struct {
Dialog *sipgo.DialogClientSession
RemoteSDP *SDPMedia
RTPSess *RTPSession
// ExtraRTPSess holds the media sockets for audio sections beyond the first,
// in m-line order. Nil for single-stream calls. OfferedStreams describes
// every audio section offered, including the first, so the leg can zip the
// peer's answer onto them positionally.
ExtraRTPSess []*RTPSession
OfferedStreams []AudioStream
// Optional RTT (T.140 / RFC 4103) media. Populated when the remote's
// answer accepts the offered m=text section. Nil otherwise.
TextRTPSess *RTPSession
// Session timer (RFC 4028) — populated when remote's 200 OK includes timers.
SessionTimer *SessionTimerParams // nil when remote didn't include timers
}
OutboundCall wraps a sipgo DialogClientSession with parsed answer SDP.
type OutboundRegistration ¶ added in v0.9.0
type OutboundRegistration struct {
// contains filtered or unexported fields
}
OutboundRegistration is the sip_register Trunk implementation. One instance per trunk; safe for concurrent access.
func NewOutboundRegistration ¶ added in v0.9.0
func NewOutboundRegistration(engine *Engine, bus *events.Bus, log *slog.Logger, cfg OutboundRegistrationConfig, p OutboundRegistrationParams) *OutboundRegistration
NewOutboundRegistration constructs a trunk in the registering state. Call Start to launch the background lifecycle.
func (*OutboundRegistration) AOR ¶ added in v0.9.0
func (r *OutboundRegistration) AOR() string
func (*OutboundRegistration) AppID ¶ added in v0.9.0
func (r *OutboundRegistration) AppID() string
func (*OutboundRegistration) Credentials ¶ added in v0.9.0
func (r *OutboundRegistration) Credentials() (string, string)
Credentials returns the trunk's digest username/password. Used by the outbound INVITE path; never exposed over the API.
func (*OutboundRegistration) FromHost ¶ added in v0.12.0
func (r *OutboundRegistration) FromHost() string
FromHost returns the AOR realm host — the domain the upstream registrar authenticated us under. Used as the host part of the From and P-Asserted-Identity URIs on outbound INVITEs placed from this trunk, so the call claims the identity the trunk actually registered rather than the engine's own public host.
No lock: r.aor is assigned once in the constructor and never mutated, the same reason AOR() reads it lock-free.
func (*OutboundRegistration) ID ¶ added in v0.9.0
func (r *OutboundRegistration) ID() string
func (*OutboundRegistration) PeerSocket ¶ added in v0.9.0
func (r *OutboundRegistration) PeerSocket() (host string, port int, transport string)
func (*OutboundRegistration) RegistrarURI ¶ added in v0.9.0
func (r *OutboundRegistration) RegistrarURI() sip.Uri
RegistrarURI exposes the configured upstream registrar URI; used by the outbound INVITE path to attach a Route header.
func (*OutboundRegistration) Snapshot ¶ added in v0.9.0
func (r *OutboundRegistration) Snapshot() TrunkView
Snapshot returns the current TrunkView. Safe to call concurrently.
func (*OutboundRegistration) Start ¶ added in v0.9.0
func (r *OutboundRegistration) Start(ctx context.Context)
Start launches the background register-and-refresh loop. Calling Start more than once is a no-op.
func (*OutboundRegistration) Stop ¶ added in v0.9.0
func (r *OutboundRegistration) Stop(ctx context.Context) error
Stop cancels the refresh loop and sends a final REGISTER with Expires: 0. Best-effort; honours ctx.
func (*OutboundRegistration) Type ¶ added in v0.9.0
func (r *OutboundRegistration) Type() TrunkType
type OutboundRegistrationConfig ¶ added in v0.9.0
type OutboundRegistrationConfig struct {
DefaultExpiresSeconds int
MinExpiresSeconds int
MaxExpiresSeconds int
RefreshRatio float64
FailureBackoffMax time.Duration
}
OutboundRegistrationConfig holds tunables shared across every sip_register trunk.
type OutboundRegistrationParams ¶ added in v0.9.0
type OutboundRegistrationParams struct {
ID string
AppID string
RegistrarURI sip.Uri
AOR sip.Uri
Username string
Password string
ContactUser string
RequestedExpiresSeconds int
}
OutboundRegistrationParams is the per-trunk creation payload (the values that survive validation of a CreateTrunkRequest of type sip_register).
type PortAllocator ¶
type PortAllocator struct {
// contains filtered or unexported fields
}
PortAllocator manages a pool of UDP ports within a configured range. It is safe for concurrent use.
func NewPortAllocator ¶
func NewPortAllocator(min, max int) (*PortAllocator, error)
NewPortAllocator creates a port allocator for the given range [min, max]. Returns nil if min and max are both 0 (use OS-assigned ports).
func (*PortAllocator) Allocate ¶
func (pa *PortAllocator) Allocate() (int, error)
Allocate returns the next available port from the pool.
func (*PortAllocator) Range ¶
func (pa *PortAllocator) Range() (int, int)
Range returns the configured min and max ports.
func (*PortAllocator) Release ¶
func (pa *PortAllocator) Release(port int)
Release returns a port back to the pool.
type RTPSession ¶
type RTPSession struct {
// contains filtered or unexported fields
}
RTPSession manages a UDP socket for RTP send/receive. It implements symmetric RTP (RFC 4961): once an RTP packet is received, the remote address is latched to the source IP:port of that packet, overriding the SDP-provided address. This is essential for NAT traversal.
func NewRTPSession ¶
func NewRTPSession() (*RTPSession, error)
NewRTPSession creates a new RTP session listening on a random UDP port. The socket is dual-stack on Linux (accepts both v4 and v6 when /proc/sys/net/ipv6/bindv6only is 0, which is the default).
func NewRTPSessionFromAllocator ¶
func NewRTPSessionFromAllocator(alloc *PortAllocator) (*RTPSession, error)
NewRTPSessionFromAllocator creates an RTP session using a port from the allocator's pool. If alloc is nil, behaves like NewRTPSession (OS-assigned).
func NewRTPSessionOnPort ¶
func NewRTPSessionOnPort(port int) (*RTPSession, error)
NewRTPSessionOnPort creates a new RTP session on a specific local port. Same dual-stack semantics as NewRTPSession.
func (*RTPSession) Close ¶
func (s *RTPSession) Close() error
Close closes the UDP connection and releases the port back to the allocator. Idempotent: subsequent calls are no-ops. Multiple rollback paths in the engine ladder can each call Close on the same session.
func (*RTPSession) LocalPort ¶
func (s *RTPSession) LocalPort() int
LocalPort returns the local UDP port this session is listening on.
func (*RTPSession) ReadRTP ¶
func (s *RTPSession) ReadRTP() (*rtp.Packet, error)
ReadRTP reads and unmarshals an RTP packet from the UDP socket. Blocks until data arrives. Implements symmetric RTP: the remote address is latched to the source IP:port of each incoming RTP packet.
func (*RTPSession) RemoteAddr ¶ added in v0.12.0
func (s *RTPSession) RemoteAddr() *net.UDPAddr
RemoteAddr returns the address media is currently sent to, or nil before the first SetRemote. Reflects symmetric-RTP latching as well as SDP negotiation.
func (*RTPSession) SendKeepalive ¶
func (s *RTPSession) SendKeepalive(payloadType uint8, count int)
SendKeepalive sends a small burst of silence RTP packets to the remote address. This is used immediately after SetRemote on outbound calls to punch through NAT devices (port-latching) before the leg's full media pipeline starts.
func (*RTPSession) SetReadDeadline ¶
func (s *RTPSession) SetReadDeadline(t time.Time) error
SetReadDeadline sets a deadline on the underlying UDP socket for reads.
type RegisterAttempt ¶ added in v0.10.0
type RegisterAttempt struct {
Request *sip.Request
AOR string
Contact string
Source string
Transport string
UserAgent string
CallID string
HasAuth bool
}
RegisterAttempt describes an inbound REGISTER surfaced to the decision callback so the client can decide whether to challenge it (e.g. based on the source address).
type RegisterDecision ¶ added in v0.10.0
type RegisterDecision struct {
Kind RegisterDecisionKind
Challenge ChallengeParams // used when Kind == RegisterChallenge
RejectCode int // used when Kind == RegisterReject; 0 => 403
RejectReason string // used when Kind == RegisterReject; "" => "Forbidden"
// MaxExpires, when > 0, caps the granted binding TTL (seconds) for a REGISTER
// this decision admits (accept, or the credentialed retry of a challenge). It
// only ever shortens: the effective grant is min(ClampExpires(requested),
// MaxExpires), still subject to the 60s floor. 0 leaves the registrar's normal
// clamp in force.
MaxExpires int
}
RegisterDecision is returned by the OnRegisterAttempt callback.
type RegisterDecisionKind ¶ added in v0.10.0
type RegisterDecisionKind int
RegisterDecisionKind is the action a VSI/REST client chooses for an inbound REGISTER it was consulted about.
const ( RegisterAccept RegisterDecisionKind = iota // bind and reply 200 OK RegisterChallenge // reply 401 with a digest challenge RegisterReject // reply with a non-2xx (default 403) )
type Registrar ¶ added in v0.8.0
type Registrar struct {
// contains filtered or unexported fields
}
Registrar is the in-memory AOR registry. Safe for concurrent use.
func NewRegistrar ¶ added in v0.8.0
NewRegistrar constructs a registrar. bus may be nil (events are dropped).
func (*Registrar) Bind ¶ added in v0.8.0
Bind inserts a new binding or refreshes an existing one (same AOR + Contact identity). When AllowMultipleContacts is false, any pre-existing bindings for the AOR are removed first (one event each, reason "replaced"). Emits sip.registration_active.
func (*Registrar) ClampExpires ¶ added in v0.8.0
ClampExpires clamps a requested expiry to [60, MaxExpiresSeconds]. A non-positive input is treated as the default.
func (*Registrar) Config ¶ added in v0.8.0
func (r *Registrar) Config() RegistrarConfig
Config returns the effective configuration (with defaults applied).
func (*Registrar) List ¶ added in v0.8.0
List returns every binding across all AORs. Order is not specified.
func (*Registrar) Lookup ¶ added in v0.8.0
Lookup returns the most-recently-refreshed binding for an AOR. The second return value is false when no binding exists.
func (*Registrar) LookupAll ¶ added in v0.8.0
LookupAll returns every binding for an AOR, sorted most-recently-refreshed first.
func (*Registrar) Start ¶ added in v0.8.0
Start launches the expiry sweeper. Returns immediately; the sweeper runs until ctx is cancelled.
func (*Registrar) UnbindAll ¶ added in v0.8.0
UnbindAll removes every binding under the AOR. One event per binding. Returns the number of removed bindings.
func (*Registrar) UnbindContact ¶ added in v0.8.0
UnbindContact removes a single contact under an AOR. Returns true if a binding was removed.
type RegistrarConfig ¶ added in v0.8.0
type RegistrarConfig struct {
DefaultExpiresSeconds int
MaxExpiresSeconds int
SweepInterval time.Duration
AllowMultipleContacts bool
}
RegistrarConfig holds tunables for the registrar.
type RemoteAudioStream ¶ added in v0.12.0
type RemoteAudioStream struct {
Index int // m-line position within the SDP
MID string
Label string
Content string
Lang string
RemoteIP string
RemotePort int
AddressFamily string // "IP4" or "IP6"
// Direction is the media-level attribute, falling back to the session-level
// one. It stays empty when neither is present; use EffectiveDirection for
// the RFC 8866 sendrecv default.
Direction string
Ptime int
RTCPMux bool
Codecs []codec.CodecType
CodecPTs map[codec.CodecType]uint8
CodecRates map[codec.CodecType]int
CodecFmtp map[codec.CodecType]string
DTMFEventPTs map[uint8]int
}
RemoteAudioStream holds the parsed parameters of one remote m=audio section. A zero RemotePort means the peer rejected or disabled the section.
func (*RemoteAudioStream) DTMFPTForRate ¶ added in v0.12.0
func (r *RemoteAudioStream) DTMFPTForRate(rate int) (uint8, bool)
DTMFPTForRate returns the telephone-event payload type advertised at the given clock rate, if any.
func (*RemoteAudioStream) EffectiveDirection ¶ added in v0.12.0
func (r *RemoteAudioStream) EffectiveDirection() string
EffectiveDirection resolves an absent direction attribute to the RFC 8866 default of sendrecv.
func (*RemoteAudioStream) PreferredDTMFEvent ¶ added in v0.12.0
func (r *RemoteAudioStream) PreferredDTMFEvent() (pt uint8, rate int, ok bool)
PreferredDTMFEvent returns the telephone-event PT and clock rate to mirror in an answer. When several were offered the lowest PT wins, for determinism.
type RemoteMLine ¶ added in v0.12.0
type RemoteMLine struct {
Index int
Media string // "audio", "video", "text", "application", ...
Proto []string // must be echoed verbatim when rejecting the section
Port int
MID string
Formats []string
// AudioIdx indexes into SDPMedia.Audio, or -1 for non-audio sections.
AudioIdx int
}
RemoteMLine records one m= section of any kind in offer order. Sections we don't handle are recorded too, so an answer can preserve the m-line count and ordering required by RFC 3264 §6.
type ReplacesParams ¶ added in v0.3.0
ReplacesParams carries the Replaces header dialog identity (RFC 3891).
func ParseReferTo ¶ added in v0.3.0
func ParseReferTo(value string) (string, *ReplacesParams, error)
ParseReferTo extracts the bare URI and (optional) Replaces from a Refer-To.
func (*ReplacesParams) String ¶ added in v0.3.0
func (p *ReplacesParams) String() string
String formats as the Replaces value embedded in a Refer-To URI.
type SDPConfig ¶
type SDPConfig struct {
LocalIP string
RTPPort int
Codecs []codec.CodecType // Offered/supported codecs in preference order
// Optional RTT (T.140 / RFC 4103) section. Set TextRTPPort != 0 to emit
// an m=text line in offers/answers/re-INVITEs. TextT140PT and TextREDPT
// are dynamic payload types; TextREDPT == 0 disables RFC 2198 redundancy.
// RTTRedundancy is the number of t140/t140/.../t140 generations declared
// in the RED fmtp.
TextRTPPort int
TextT140PT uint8
TextREDPT uint8
RTTRedundancy int
// AMRWBOctetAligned controls the AMR-WB fmtp emitted for an offer/answer:
// true emits "octet-align=1", false emits no octet-align (RFC 4867 default,
// bandwidth-efficient). On answers it must echo the peer's negotiated format.
AMRWBOctetAligned bool
// AMRWBModeSet, when non-empty (e.g. "0,1,2"), adds a "mode-set=..." AMR-WB
// fmtp param. Used on answers to echo the peer's negotiated mode-set per
// RFC 4867; left empty on offers (we accept all modes on receive).
AMRWBModeSet string
// AMRNBOctetAligned controls the AMR-NB fmtp emitted for an offer/answer:
// true emits "octet-align=1", false emits no octet-align (RFC 4867 default,
// bandwidth-efficient). On answers it must echo the peer's negotiated format.
AMRNBOctetAligned bool
// AMRNBModeSet, when non-empty (e.g. "0,4,7"), adds a "mode-set=..." AMR-NB
// fmtp param. Used on answers to echo the peer's negotiated mode-set per
// RFC 4867; left empty on offers (we accept all modes on receive).
AMRNBModeSet string
// DTMFPT, when non-zero, is the telephone-event (RFC 4733) PT to advertise
// in the generated SDP. Zero defaults to 101. In answers callers should set
// this from the remote offer to mirror the offerer's choice.
DTMFPT uint8
// DTMFClockRate, when non-zero, is the telephone-event clock rate (Hz) to
// advertise. Zero defaults to TelephoneEventClockRate(selected codec). In
// answers callers must set this from the remote offer so RFC 3264
// offer/answer semantics hold — phones like Fanvil pin telephone-event at
// 8 kHz regardless of audio codec, and unilaterally upgrading to 16 kHz
// breaks their DTMF.
DTMFClockRate int
// Streams, when non-empty, replaces the single audio section the generators
// would otherwise derive from RTPPort/Codecs/AMR*/DTMF*. Sections are
// emitted in slice order; a zero Port rejects one per RFC 3264 §6. The
// Text* fields still control the m=text section.
Streams []AudioStream
}
SDPConfig holds local media parameters for SDP generation.
type SDPMedia ¶
type SDPMedia struct {
RemoteIP string
RemotePort int
AddressFamily string // "IP4" or "IP6" (from c= line); empty if not present
Codecs []codec.CodecType // Codecs from m= line, in offer order
CodecPTs map[codec.CodecType]uint8 // Actual PT for each codec from remote SDP
CodecRates map[codec.CodecType]int // Clock rate (Hz) for each codec, from a=rtpmap; falls back to codec default
CodecFmtp map[codec.CodecType]string // Raw a=fmtp params for each codec (e.g. AMR-WB "octet-align=1; mode-set=...")
Ptime int // ms, default 20
Direction string // "sendrecv", "sendonly", "recvonly", "inactive"; empty = sendrecv
DTMFEventPTs map[uint8]int // telephone-event (RFC 4733) PT -> clock rate, as advertised by the remote
// Audio holds every m=audio section in SDP order, including ones the peer
// rejected with port 0. MLines holds every m= section of any kind, so an
// answer can preserve the count and ordering RFC 3264 §6 requires.
Audio []RemoteAudioStream
MLines []RemoteMLine
// PrimaryAudio indexes the Audio entry the scalar fields above mirror: the
// first section with a non-zero port, or -1 when there is none. The scalars
// are written only by syncPrimary.
PrimaryAudio int
// Text (RTT, T.140 / RFC 4103). Non-nil when the remote SDP carried an
// m=text line with a non-zero port. A port of zero (peer rejecting the
// text section per RFC 3264) leaves this field nil.
Text *SDPTextMedia
}
SDPMedia holds parsed remote media parameters.
func ParseSDPMessage ¶ added in v0.12.0
func ParseSDPMessage(msg BodyCarrier) (*SDPMedia, error)
ParseSDPMessage parses the session description carried by a SIP message, transparently unwrapping a multipart body.
func (*SDPMedia) AudioByMID ¶ added in v0.12.0
func (m *SDPMedia) AudioByMID(mid string) (*RemoteAudioStream, bool)
AudioByMID returns the audio section carrying the given a=mid value.
func (*SDPMedia) DTMFPTForRate ¶ added in v0.9.0
DTMFPTForRate returns the remote telephone-event payload type advertised at the given clock rate, if any.
func (*SDPMedia) PreferredDTMFEvent ¶ added in v0.9.0
PreferredDTMFEvent returns the telephone-event PT and clock rate to mirror in an answer (RFC 3264 offer/answer). When the remote advertised multiple telephone-event lines, the lowest PT wins for determinism. Returns (0, 0, false) when no telephone-event was offered.
func (*SDPMedia) PrimaryStream ¶ added in v0.12.0
func (m *SDPMedia) PrimaryStream() *RemoteAudioStream
PrimaryStream returns the audio section the scalar fields mirror, or nil when every offered audio section was rejected.
type SDPTextMedia ¶ added in v0.6.0
type SDPTextMedia struct {
RemoteIP string
RemotePort int
T140PT uint8 // 0 if no t140/1000 advertised
REDPT uint8 // 0 if no red/1000 advertised
Direction string
}
SDPTextMedia holds parsed remote RTT parameters.
type SIPRECSignals ¶ added in v0.12.0
type SIPRECSignals struct {
Required bool // Require/Proxy-Require: siprec
FeatureTag bool // Contact carries +sip.src
HasMetadata bool // the body carries an rs-metadata part
}
SIPRECSignals describes how an INVITE announced itself as a recording session. Conformance varies between SBCs, so each signal is reported separately and the caller decides what to do with a partial claim.
func DetectSIPREC ¶ added in v0.12.0
func DetectSIPREC(call *InboundCall) SIPRECSignals
DetectSIPREC classifies an inbound INVITE against the three RFC 7866 signals.
func (SIPRECSignals) Claimed ¶ added in v0.12.0
func (s SIPRECSignals) Claimed() bool
Claimed reports whether the INVITE claims to be a recording session at all.
type SIPRegisterTrunkView ¶ added in v0.9.0
type SIPRegisterTrunkView struct {
RegistrarURI string `json:"registrar_uri"`
AOR string `json:"aor"`
Username string `json:"username,omitempty"`
ContactURI string `json:"contact_uri,omitempty"`
RequestedExpiresSeconds int `json:"requested_expires_seconds"`
GrantedExpiresSeconds int `json:"granted_expires_seconds,omitempty"`
LastRegisteredAt string `json:"last_registered_at,omitempty"`
NextRefreshAt string `json:"next_refresh_at,omitempty"`
CallID string `json:"call_id,omitempty"`
CSeq uint32 `json:"cseq,omitempty"`
// SourceAddress is the host:port the registrar's most recent response
// actually came from. Initially set from the configured RegistrarURI;
// updated to the real transport address (which may differ from the URI
// when DNS / load-balancing fronts the registrar) on each 2xx response.
// Used as the key for tagging inbound INVITEs back to this trunk.
SourceAddress string `json:"source_address,omitempty"`
}
SIPRegisterTrunkView holds the sip_register-specific runtime fields. Credentials (password) are never exposed.
type SessionTimerParams ¶
type SessionTimerParams struct {
Interval uint32 // Session-Expires delta-seconds
Refresher string // "uac" or "uas"
MinSE uint32 // Min-SE value (0 = not present)
}
SessionTimerParams holds parsed Session-Expires / Min-SE values from a SIP request or response.
type SlotAction ¶ added in v0.12.0
type SlotAction uint8
SlotAction is what an answer does with one offered m= section.
const ( // SlotAccept negotiates the section and carries media on it. SlotAccept SlotAction = iota // SlotReject emits a port-0 placeholder so the answer keeps the offer's // m-line count and ordering. SlotReject // SlotSkip leaves the section to the caller (the m=text path). SlotSkip // SlotOmit drops the section from the answer entirely. Only produced when // StrictMLines is off, for backward compatibility. SlotOmit )
type SlotPlan ¶ added in v0.12.0
type SlotPlan struct {
Index int
Media string
Proto []string
Action SlotAction
Reason string // why a section was rejected; "" when accepted
// AudioIdx indexes into SDPMedia.Audio, or -1 for non-audio sections.
AudioIdx int
Codec codec.CodecType
PT uint8
Direction string // the direction our answer must carry
MID string
Label string
Content string
Lang string
}
SlotPlan is the decision for one offered m= section.
func AcceptedAudio ¶ added in v0.12.0
AcceptedAudio returns the plans that negotiated an audio section.
func PlanAnswer ¶ added in v0.12.0
func PlanAnswer(offer *SDPMedia, opts AnswerOptions) []SlotPlan
PlanAnswer decides, for every m= section in an offer, what the answer does with it. It is pure: no ports are allocated and no media is touched, so the caller can reject a section it cannot materialize and still emit a conformant answer.
RFC 3264 §6 governs the shape: one answer section per offered section, in the same order, with port 0 for anything refused.
type SlotState ¶ added in v0.12.0
type SlotState uint8
SlotState is the negotiation state of one m-line slot.
const ( // SlotPending is offered by us but not yet answered by the peer. SlotPending SlotState = iota // SlotActive is negotiated and carrying (or ready to carry) media. SlotActive // SlotTombstone is disabled forever: the slot keeps its position and is // re-emitted with port 0 in every subsequent offer (RFC 3264 §8.2). SlotTombstone )
type Trunk ¶ added in v0.9.0
type Trunk interface {
ID() string
Type() TrunkType
// AOR returns the canonical identity used to match outbound POST /v1/legs
// `from` against this trunk. Empty when the trunk type has no AOR concept.
AOR() string
// PeerSocket returns the upstream peer's transport address used to tag
// inbound INVITEs delivered by this trunk. Returns empty host with port 0
// when not yet known.
PeerSocket() (host string, port int, transport string)
AppID() string
Snapshot() TrunkView
// Start launches the background lifecycle (REGISTER + refresh for
// sip_register). Returns immediately.
Start(ctx context.Context)
// Stop tears the trunk down (de-register for sip_register). Best-effort;
// honours ctx for timeout.
Stop(ctx context.Context) error
}
Trunk is the abstract resource managed by the TrunkManager. Each concrete type (sip_register, future ip_ip, ...) implements this interface; lookups over the manager are type-agnostic.
type TrunkManager ¶ added in v0.9.0
type TrunkManager struct {
// contains filtered or unexported fields
}
TrunkManager is the concurrent registry of all SIP trunks (sip_register today; ip_ip etc. in the future). Lookups are type-agnostic and indexed by id, canonical AOR, and upstream peer socket.
func NewTrunkManager ¶ added in v0.9.0
func NewTrunkManager() *TrunkManager
func (*TrunkManager) Add ¶ added in v0.9.0
func (m *TrunkManager) Add(t Trunk)
Add inserts a trunk, indexing it by id, AOR (when non-empty), and peer socket (when known).
func (*TrunkManager) Get ¶ added in v0.9.0
func (m *TrunkManager) Get(id string) Trunk
Get returns the trunk with the given id, or nil.
func (*TrunkManager) List ¶ added in v0.9.0
func (m *TrunkManager) List() []Trunk
List returns every registered trunk in indeterminate order.
func (*TrunkManager) LookupByAORUser ¶ added in v0.9.0
func (m *TrunkManager) LookupByAORUser(user string) Trunk
LookupByAORUser returns a trunk whose AOR user-part matches the given user string. Falls back when the caller only supplied a bare user on `from` (e.g. POST /v1/legs with `from: "alice"`) — the engine's publicHost won't match the trunk's upstream domain. Returns nil if no match; if multiple trunks have the same user, returns one arbitrarily.
func (*TrunkManager) LookupByFromAOR ¶ added in v0.9.0
func (m *TrunkManager) LookupByFromAOR(aor string) Trunk
LookupByFromAOR returns the trunk whose canonical AOR matches the given URI (after canonicalisation), or nil. The input may be a raw SIP URI string ("sip:alice@host") or already-canonical form.
func (*TrunkManager) LookupByPeerSocket ¶ added in v0.9.0
func (m *TrunkManager) LookupByPeerSocket(host string, port int) Trunk
LookupByPeerSocket returns the trunk whose upstream peer matches the given transport address, or nil. Matches first on full host:port, then on host alone (for cases where the peer's ephemeral source port differs from the configured registrar port).
func (*TrunkManager) RefreshIndex ¶ added in v0.9.0
func (m *TrunkManager) RefreshIndex(id string)
RefreshIndex re-indexes the trunk under its current AOR and peer socket. Call this after a trunk's PeerSocket() value becomes known (e.g. after the first successful REGISTER reveals the registrar's actual transport addr).
func (*TrunkManager) Remove ¶ added in v0.9.0
func (m *TrunkManager) Remove(id string) Trunk
Remove removes a trunk by id (does not Stop it). Returns the removed trunk, or nil if absent.
func (*TrunkManager) Shutdown ¶ added in v0.9.0
func (m *TrunkManager) Shutdown(ctx context.Context)
Shutdown stops every trunk in parallel, honouring ctx. After return the manager is empty.
type TrunkStatus ¶ added in v0.9.0
type TrunkStatus string
TrunkStatus is the runtime state of a trunk's upstream connection.
const ( TrunkStatusRegistering TrunkStatus = "registering" TrunkStatusActive TrunkStatus = "active" TrunkStatusFailed TrunkStatus = "failed" TrunkStatusUnregistering TrunkStatus = "unregistering" TrunkStatusExpired TrunkStatus = "expired" )
type TrunkType ¶ added in v0.9.0
type TrunkType string
TrunkType discriminates the upstream connection style of a SIP trunk. Only TrunkTypeSIPRegister is implemented today; other values are reserved for the OpenAPI/AsyncAPI contract and rejected at request time.
const ( // TrunkTypeSIPRegister: VoiceBlender acts as a UAC and REGISTERs to an // upstream registrar, refreshing periodically; calls in either direction // flow through that registered identity. TrunkTypeSIPRegister TrunkType = "sip_register" // TrunkTypeIPIP: reserved for static-IP peering (no REGISTER). Not yet // implemented; the trunks handler returns 501 when requested. TrunkTypeIPIP TrunkType = "ip_ip" )
type TrunkView ¶ added in v0.9.0
type TrunkView struct {
ID string `json:"id"`
Type TrunkType `json:"type"`
AppID string `json:"app_id,omitempty"`
Status TrunkStatus `json:"status"`
LastError string `json:"last_error,omitempty"`
CreatedAt string `json:"created_at"`
SIPRegister *SIPRegisterTrunkView `json:"sip_register,omitempty"`
IPIP *IPIPTrunkView `json:"ip_ip,omitempty"`
}
TrunkView is the JSON-friendly snapshot of a trunk's current state. Each type populates its own sub-struct; only one of `SIPRegister` / `IPIP` is non-nil per snapshot.
type WhatsAppInviteOptions ¶ added in v0.5.0
type WhatsAppInviteOptions struct {
// FromNumber is the business phone number in E.164. Used as the From
// URI user (with leading '+'). When DigestUsername is empty it also
// serves as the digest auth username (with the '+' stripped, per Meta).
FromNumber string
DigestUsername string // optional override; defaults to FromNumber without '+'
Password string
SDPOffer []byte
Headers []sip.Header
}
type WhatsAppOutboundCall ¶ added in v0.5.0
type WhatsAppOutboundCall struct {
Dialog *sipgo.DialogClientSession
AnswerSDP []byte
}