Parrot.Sip.Headers.Via (Parrot Platform v0.0.1-alpha.2)

Module for working with SIP Via headers as defined in RFC 3261 Section 20.42.

The Via header is used to record the route taken by a SIP request and to route responses back along the same path. Each SIP element (proxy or UAC) that sends a request adds a Via header with its own address, while responses follow the Via headers in reverse order.

The Via header contains:

  • Protocol and version (e.g., "SIP/2.0")
  • Transport protocol (e.g., UDP, TCP, TLS)
  • Sent-by address (hostname or IP address and optional port)
  • Branch parameter (a unique transaction identifier)
  • Optional parameters (e.g., received, rport, maddr)

Via headers are critical for:

  • Loop detection (using the branch parameter)
  • Response routing (following the Via chain backwards)
  • NAT traversal (using received/rport parameters)

The branch parameter starting with "z9hG4bK" indicates compliance with RFC 3261 (Section 8.1.1.7) and forms part of the transaction identifier.

This module supports both IPv4 and IPv6 addresses. IPv6 addresses in Via headers must be enclosed in square brackets as specified in RFC 3261 Section 25.1.

References:

  • RFC 3261 Section 8.1.1.7: Transaction Identifier
  • RFC 3261 Section 18.2.1: Sending Responses
  • RFC 3261 Section 20.42: Via Header Field
  • RFC 3261 Section 25.1: IPv6 References
  • RFC 3581: Symmetric Response Routing (rport parameter)

Summary

Functions

Gets the branch parameter from a Via header.

Ensures that the branch parameter in a Via header is RFC 3261 compliant.

Converts a Via header to a string representation.

Formats a list of Via headers for SIP message serialization.

Generates a unique branch parameter for a Via header.

Gets a parameter from a Via header.

Checks if a parameter exists in the Via header.

Creates a new Via header with a randomly generated branch parameter.

Parses a Via header string into a Via struct.

Gets the received parameter from a Via header.

Checks if the branch parameter in a Via header is RFC 3261 compliant.

Gets the rport parameter from a Via header.

Returns a tuple {topmost, rest} where topmost is the first Via header and rest is the remaining Via structs (or nil if only one) from a SIP message struct.

Returns the topmost (first) Via header from a SIP message struct.

Adds or updates a parameter in a Via header.

Types

t()

@type t() :: %Parrot.Sip.Headers.Via{
  host: String.t(),
  host_type: :hostname | :ipv4 | :ipv6,
  parameters: map(),
  port: integer() | nil,
  protocol: String.t(),
  transport: atom(),
  version: String.t()
}

Functions

branch(via)

Gets the branch parameter from a Via header.

Returns the branch parameter value or nil if not present.

Examples

iex> via = Parrot.Sip.Headers.Via.new("example.com", "udp", nil, %{"branch" => "z9hG4bKabc"})
iex> Parrot.Sip.Headers.Via.branch(via)
"z9hG4bKabc"

ensure_rfc3261_branch(via)

@spec ensure_rfc3261_branch(t()) :: t()

Ensures that the branch parameter in a Via header is RFC 3261 compliant.

If the branch parameter is already compliant, returns the Via header unchanged. If not, adds the magic cookie "z9hG4bK" to the beginning of the branch. If no branch is present, generates a new one.

Examples

iex> via = Parrot.Sip.Headers.Via.new("example.com")
iex> via = Parrot.Sip.Headers.Via.with_parameter(via, "branch", "abc123")
iex> via = Parrot.Sip.Headers.Via.ensure_rfc3261_branch(via)
iex> Parrot.Sip.Headers.Via.branch(via)
"z9hG4bKabc123"

format(via)

@spec format(t()) :: String.t()

Converts a Via header to a string representation.

Examples

iex> via = Parrot.Sip.Headers.Via.new("example.com", :udp, 5060, %{"branch" => "z9hG4bKabc"})
iex> Parrot.Sip.Headers.Via.format(via)
"SIP/2.0/UDP example.com:5060;branch=z9hG4bKabc"

format_list(via_list)

@spec format_list([t()]) :: String.t()

Formats a list of Via headers for SIP message serialization.

Multiple Via headers are formatted on separate lines in SIP messages.

Examples

iex> via1 = Parrot.Sip.Headers.Via.new("proxy1.com", :udp, 5060)
iex> via2 = Parrot.Sip.Headers.Via.new("proxy2.com", :tcp, 5061)
iex> Parrot.Sip.Headers.Via.format_list([via1, via2])
"SIP/2.0/UDP proxy1.com:5060, SIP/2.0/TCP proxy2.com:5061"

generate_branch()

@spec generate_branch() :: String.t()

Generates a unique branch parameter for a Via header.

Delegates to Parrot.Sip.Branch.generate/0.

Examples

iex> Parrot.Sip.Headers.Via.generate_branch()
"z9hG4bK..."  # output will vary

get_parameter(via, name)

@spec get_parameter(t(), String.t()) :: String.t() | nil

Gets a parameter from a Via header.

Examples

iex> via = Parrot.Sip.Headers.Via.new("example.com", "udp", nil, %{"rport" => "5060"})
iex> Parrot.Sip.Headers.Via.get_parameter(via, "rport")
"5060"

has_parameter?(via, name)

@spec has_parameter?(t(), String.t()) :: boolean()

Checks if a parameter exists in the Via header.

Examples

iex> via = Parrot.Sip.Headers.Via.new("example.com")
iex> via = Parrot.Sip.Headers.Via.with_parameter(via, "branch", "z9hG4bKabc")
iex> Parrot.Sip.Headers.Via.has_parameter?(via, "branch")
true

iex> Parrot.Sip.Headers.Via.has_parameter?(via, "received")
false

new(host, transport \\ "udp", port \\ nil, parameters \\ %{})

@spec new(String.t(), atom() | String.t(), integer() | nil, map()) :: t()

Creates a new Via header.

Examples

iex> Parrot.Sip.Headers.Via.new("example.com")
%Parrot.Sip.Headers.Via{
  protocol: "SIP",
  version: "2.0",
  transport: :udp,
  host: "example.com",
  port: nil,
  host_type: :hostname,
  parameters: %{}
}

new_with_branch(host, transport \\ "udp", port \\ nil, parameters \\ %{})

@spec new_with_branch(String.t(), String.t(), integer() | nil, map()) :: t()

Creates a new Via header with a randomly generated branch parameter.

Examples

iex> via = Parrot.Sip.Headers.Via.new_with_branch("example.com")
iex> Parrot.Sip.Headers.Via.branch(via) |> String.starts_with?("z9hG4bK")
true

parse(string)

@spec parse(String.t()) :: t()

Parses a Via header string into a Via struct.

Examples

iex> Parrot.Sip.Headers.Via.parse("SIP/2.0/UDP server10.biloxi.com:5060;branch=z9hG4bKnashds8")
%Parrot.Sip.Headers.Via{protocol: "SIP", version: "2.0", transport: :udp, host: "server10.biloxi.com", port: 5060, host_type: :hostname, parameters: %{"branch" => "z9hG4bKnashds8"}}

iex> Parrot.Sip.Headers.Via.parse("SIP/2.0/UDP [2001:db8::1]:5060;branch=z9hG4bK776asdhds")
%Parrot.Sip.Headers.Via{protocol: "SIP", version: "2.0", transport: :udp, host: "[2001:db8::1]", port: 5060, host_type: :ipv6, parameters: %{"branch" => "z9hG4bK776asdhds"}}

received(via)

@spec received(t()) :: String.t() | nil

Gets the received parameter from a Via header.

Examples

iex> via = Parrot.Sip.Headers.Via.new("example.com", "udp", nil, %{"received" => "203.0.113.1"})
iex> Parrot.Sip.Headers.Via.received(via)
"203.0.113.1"

rfc3261_compliant_branch?(via)

@spec rfc3261_compliant_branch?(t()) :: boolean()

Checks if the branch parameter in a Via header is RFC 3261 compliant.

A branch parameter is RFC 3261 compliant if it starts with the magic cookie "z9hG4bK".

Examples

iex> via = Parrot.Sip.Headers.Via.new("example.com")
iex> via = Parrot.Sip.Headers.Via.with_parameter(via, "branch", "z9hG4bKabc123")
iex> Parrot.Sip.Headers.Via.rfc3261_compliant_branch?(via)
true

iex> via = Parrot.Sip.Headers.Via.new("example.com")
iex> via = Parrot.Sip.Headers.Via.with_parameter(via, "branch", "abc123")
iex> Parrot.Sip.Headers.Via.rfc3261_compliant_branch?(via)
false

rport(via)

@spec rport(t()) :: String.t() | nil

Gets the rport parameter from a Via header.

Examples

iex> via = Parrot.Sip.Headers.Via.new("example.com", "udp", nil, %{"rport" => "5060"})
iex> Parrot.Sip.Headers.Via.rport(via)
"5060"

take_topmost(arg1)

@spec take_topmost(map()) :: {t(), [t()] | nil} | {t(), nil} | {nil, nil}

Returns a tuple {topmost, rest} where topmost is the first Via header and rest is the remaining Via structs (or nil if only one) from a SIP message struct.

Examples

iex> Parrot.Sip.Headers.Via.take_topmost(message)
{%Parrot.Sip.Headers.Via{host: "host1", ...}, [rest...]}

topmost(arg1)

@spec topmost(map()) :: t() | nil

Returns the topmost (first) Via header from a SIP message struct.

Examples

iex> Parrot.Sip.Headers.Via.topmost(message)
%Parrot.Sip.Headers.Via{host: "host1", ...}

with_parameter(via, name, value)

@spec with_parameter(t(), String.t(), String.t()) :: t()

Adds or updates a parameter in a Via header.

Examples

iex> via = Parrot.Sip.Headers.Via.new("example.com")
iex> Parrot.Sip.Headers.Via.with_parameter(via, "received", "192.0.2.1")
%Parrot.Sip.Headers.Via{parameters: %{"received" => "192.0.2.1"}, ...}