# `XGW.ICE.Binding`
[🔗](https://github.com/Lazarus404/xgw/blob/main/lib/xgw/ice/binding.ex#L1)

Builds and validates STUN Binding messages used as ICE connectivity checks.

## Plain language

**STUN** (Session Traversal Utilities for NAT) is a small request/response
protocol often carried in UDP. ICE uses STUN **Binding** requests as
"can you hear me on this address?" probes. Successful checks prove a
candidate pair works; attributes such as `ICE-CONTROLLING` /
`ICE-CONTROLLED` and `USE-CANDIDATE` carry nomination and role info.

Integrity uses the SDP `ice-pwd` as a MESSAGE-INTEGRITY key. This module is
shared by ICE-lite (mostly inbound responses) and ICE-full (outbound checks
plus responses).

## RFCs

* RFC 8489 -- STUN Binding request/response, MESSAGE-INTEGRITY, FINGERPRINT
* RFC 8445 -- ICE connectivity checks, USERNAME form, role attrs, USE-CANDIDATE

# `creds`

```elixir
@type creds() :: %{
  optional(:ice_ufrag) =&gt; String.t(),
  optional(:ice_pwd) =&gt; String.t(),
  optional(:remote_ice_ufrag) =&gt; String.t() | nil,
  optional(:remote_ice_pwd) =&gt; String.t() | nil,
  optional(:tiebreaker) =&gt; binary(),
  optional(:role) =&gt; :controlling | :controlled
}
```

Credential map for STUN encode/decode during ICE checks.

## Fields

* `:ice_ufrag` / `:ice_pwd` - local ICE credentials from SDP
* `:remote_ice_ufrag` / `:remote_ice_pwd` - remote credentials (optional until known)
* `:tiebreaker` - ICE role conflict tie-breaker
* `:role` - `:controlling` or `:controlled`

# `build_check_request`

```elixir
@spec build_check_request(creds(), non_neg_integer(), keyword()) :: binary()
```

Builds an outbound ICE connectivity check Binding request.

USERNAME is `remote:local` (peer ufrag first); MESSAGE-INTEGRITY uses the
remote pwd (RFC 8445 pt.7.2.2).

# `build_success`

```elixir
@spec build_success(
  XMediaLib.Stun.t(),
  {:inet.ip_address(), :inet.port_number()},
  creds()
) :: binary()
```

Builds a Binding success response for an inbound request.

# `connectivity_check?`

```elixir
@spec connectivity_check?(XMediaLib.Stun.t()) :: boolean()
```

Returns true when the request is an ICE connectivity check (role attrs present).

# `decode`

```elixir
@spec decode(binary(), creds()) :: {:ok, XMediaLib.Stun.t()} | {:error, term()}
```

Decodes STUN Binding packets.

Tries local `ice_pwd` first (ICE-lite inbound checks), then `remote_ice_pwd`
when set (peer-signed consent / full-ICE responses).

# `use_candidate?`

```elixir
@spec use_candidate?(XMediaLib.Stun.t()) :: boolean()
```

Returns true when the request includes USE-CANDIDATE (nomination).

# `valid_username?`

```elixir
@spec valid_username?(XMediaLib.Stun.t(), creds()) :: boolean()
```

Returns true when USERNAME matches RFC 8445 inbound checks.

The sender sets USERNAME to `remote_candidate_ufrag:local_candidate_ufrag`,
which at the receiver is `ice_ufrag:remote_ice_ufrag`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
