outbound

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: 23 Imported by: 0

Documentation

Overview

Package outbound implements SIP UAC (outbound) signaling without media binding.

It shares the UDP socket with inbound UAS via SignalingSender (see gateway.Endpoint). Responses are routed through Manager.HandleSIPResponse on stack.EndpointConfig.OnSIPResponse.

Supported signaling:

  • INVITE / provisional / 200 OK + ACK
  • CANCEL (RFC 3261 §9.1, with retransmit)
  • BYE (in-dialog)
  • UPDATE session-timer refresh (RFC 4028 UAC refresher)
  • UDP / TCP / TLS transport selection and connection pooling

Index

Constants

View Source
const (
	DialEventInvited     = "invited"
	DialEventProvisional = "provisional"
	DialEventEstablished = "established"
	DialEventFailed      = "failed"
)

Variables

View Source
var (
	// ErrNoSignalingSender is returned when Dial is called before BindSender.
	ErrNoSignalingSender = errors.New("sip/outbound: signaling sender not bound")
	// ErrNotImplemented marks transfer/bridge paths not yet wired.
	ErrNotImplemented = errors.New("sip/outbound: not implemented")
)

Functions

This section is empty.

Types

type DialEvent

type DialEvent struct {
	CallID        string
	CorrelationID string
	Scenario      Scenario
	State         string
	StatusCode    int
	Reason        string
	At            time.Time
	RequestURI    string
	StatusText    string
	RemoteAddr    string
}

DialEvent streams dial lifecycle transitions.

type DialRequest

type DialRequest struct {
	Scenario      Scenario
	Target        DialTarget
	CorrelationID string
	ScriptID      string

	CallerUser        string
	CallerDisplayName string

	AssertedIdentityURI         string
	AssertedIdentityDisplayName string
	PrivacyTokens               []string
	HistoryInfo                 []historyinfo.Entry
	Diversion                   []historyinfo.Diversion

	// RTPPort is advertised in the SDP offer only (signaling demo default 10000).
	RTPPort int
	// Codecs overrides the default outbound offer list when non-empty.
	Codecs []sdp.Codec
	// OfferSRTP adds SDES a=crypto to the SDP offer (RTP/SAVPF).
	OfferSRTP bool
}

DialRequest is one outbound signaling attempt (no media binding).

type DialTarget

type DialTarget struct {
	RequestURI        string // e.g. sip:1001@192.168.1.10;user=phone
	SignalingAddr     string // host:port of proxy or UAS
	CallerUser        string
	CallerDisplayName string
	Transport         Transport
}

DialTarget describes the next SIP hop for an outbound INVITE.

func DialTargetFromReferTo

func DialTargetFromReferTo(referTo string) (DialTarget, error)

DialTargetFromReferTo parses a Refer-To header value (possibly angle-bracketed) into a DialTarget. Host and port for SignalingAddr come from the SIP URI authority; default port is 5060 when omitted.

type EstablishedLeg

type EstablishedLeg struct {
	CallID        string
	Scenario      Scenario
	CorrelationID string
	CreatedAt     time.Time

	FromHeader          string
	ToHeader            string
	RemoteSignalingAddr string
	CSeqInvite          string

	Answer *sdp.Info
}

EstablishedLeg is delivered after 200 OK + ACK (signaling complete).

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager owns outbound SIP legs keyed by Call-ID.

func NewManager

func NewManager(cfg ManagerConfig) *Manager

NewManager constructs a manager; call BindSender before Dial.

func (*Manager) BindSender

func (m *Manager) BindSender(s SignalingSender)

BindSender wires the UDP signaling path (required for Dial).

func (*Manager) CleanupLegIfPresent

func (m *Manager) CleanupLegIfPresent(callID, reasonClass string)

CleanupLegIfPresent removes outbound leg state when no longer needed, e.g. after remote BYE. reasonClass is the bounded enum extracted from the peer's RFC 3326 Reason header (empty string → default "normal"). Both the BYE counter and the CDR hangup_reason inherit this value.

func (*Manager) ClosePool

func (m *Manager) ClosePool() error

ClosePool shuts pooled TCP/TLS connections.

func (*Manager) Dial

func (m *Manager) Dial(ctx context.Context, req DialRequest) (callID string, err error)

Dial starts an outbound INVITE. Returns Call-ID on success.

func (*Manager) HandleSIPResponse

func (m *Manager) HandleSIPResponse(resp *stack.Message, addr *net.UDPAddr)

HandleSIPResponse routes inbound responses to outbound legs.

func (*Manager) SendBYE

func (m *Manager) SendBYE(callID string) error

SendBYE sends an in-dialog BYE for an established outbound leg (after 200 OK to INVITE).

func (*Manager) SendCANCEL

func (m *Manager) SendCANCEL(callID string) error

SendCANCEL emits a SIP CANCEL for a not-yet-established outbound INVITE leg (transfer-agent ring, campaign abandon, etc). Safe to call concurrently with the response handler; idempotent (second call is a no-op).

Behavior:

  • if the INVITE has not yet received its first 1xx provisional, the CANCEL is QUEUED (RFC 3261 §9.1) and fires either when 1xx arrives or after a 500ms grace timer, whichever comes first
  • if 1xx already received, CANCEL goes on the wire immediately and a retransmit goroutine starts (T1=500ms, capped at T2=4s, 6 attempts) per RFC 3261 §17.1.2
  • second call is a no-op (cancelSent CompareAndSwap guard) so the inbound-BYE path and ring-timeout path can both invoke safely

Returns nil on success (queued or sent). Common error reasons:

  • unknown call-id (leg already torn down / never existed)
  • leg already established (CANCEL is illegal — caller should use SendBYE instead)

type ManagerConfig

type ManagerConfig struct {
	LocalIP         string // SDP c= line
	SIPHost         string // Via / Contact host
	SIPPort         int
	FromUser        string
	FromDisplayName string
	DefaultRTPPort  int

	OnEstablished         func(EstablishedLeg)
	OnEvent               func(DialEvent)
	OnDialogCallIDAdopted func(oldID, newID, correlationID string)
	OnSignalingSent       func(*stack.Message, *net.UDPAddr)
	TLSConfig             *tls.Config
}

ManagerConfig configures outbound signaling (no RTP/media).

type Scenario

type Scenario string

Scenario classifies why an outbound leg exists.

const (
	ScenarioCampaign      Scenario = "campaign"
	ScenarioTransferAgent Scenario = "transfer_agent"
	ScenarioCallback      Scenario = "callback"
	ScenarioManual        Scenario = "manual"
)

type SignalingSender

type SignalingSender interface {
	SendSIP(msg *stack.Message, addr *net.UDPAddr) error
}

SignalingSender sends SIP on a shared UDP socket.

type Transport

type Transport string

Transport identifies the SIP signaling transport for an outbound leg. Stringer values are the lowercase RFC 3261 §7.1 form used in Via headers and Request-URI ;transport= parameters.

const (
	// TransportUDP is unreliable datagram (RFC 3261 §18.1). Default.
	TransportUDP Transport = "udp"
	// TransportTCP is connection-oriented byte stream framed by
	// Content-Length (RFC 3261 §18.2). Connection reuse follows
	// RFC 5923.
	TransportTCP Transport = "tcp"
	// TransportTLS is TCP wrapped in TLS, paired with the SIPS URI
	// scheme on Contact / Via (RFC 3261 §26.2.1).
	TransportTLS Transport = "tls"
	// TransportUnset means "no preference" — selection falls through
	// to the next precedence layer or to TransportUDP.
	TransportUnset Transport = ""
)

func ResolveTransport

func ResolveTransport(target DialTarget) Transport

ResolveTransport applies the precedence rules above:

URI param > target.Transport (trunk config) > TransportUDP

Always returns a valid (non-Unset) transport.

func (Transport) IsConnectionOriented

func (t Transport) IsConnectionOriented() bool

IsConnectionOriented reports whether the transport requires per-target conn lifecycle management (TCP/TLS) versus the shared-socket model (UDP).

func (Transport) IsTLS

func (t Transport) IsTLS() bool

IsTLS reports whether the transport requires TLS handshaking. The caller should also use the `sips:` URI scheme in Contact / Via when this returns true (RFC 3261 §26.2.1).

func (Transport) IsValid

func (t Transport) IsValid() bool

IsValid reports whether t is one of the recognised transports. TransportUnset is NOT valid (caller should resolve before calling).

func (Transport) ViaToken

func (t Transport) ViaToken() string

ViaToken returns the Via header transport token, e.g. "SIP/2.0/UDP". Always uppercase per RFC 3261 §7.1.

Jump to

Keyboard shortcuts

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