skip to content

How does a STUN Binding request let a host behind a NAT learn its server-reflexive address, and why does the response XOR-encode that address?

level: middleimportance: should knowfreq 31%

answer

  1. the server sees the rewritten source
  2. copied into the response body
  3. outermost NAT only
  4. middleboxes rewriting address-like bytes
  5. the magic cookie as the key

basics

~20 s

The STUN server copies the source address and port it saw on the Binding request, the NAT's public mapping, into XOR-MAPPED-ADDRESS. XOR-encoding hides that value from NAT ALGs that would otherwise rewrite it inside the payload.

solid answer

~50 s

A STUN client sends a Binding request (RFC 8489) from the local address and port it will later use for data. Each NAT on the way rewrites the source, so the server sees the public address and port of the NAT closest to it, the **server-reflexive** transport address, and copies it into an `XOR-MAPPED-ADDRESS` attribute in the success response. On the way back the NAT rewrites the IP and UDP headers but not the payload, so the client reads its mapping from the body. The XOR exists because some NATs ran a well-meaning but misguided ALG that rewrote any 32-bit copy of their public address, which broke RFC 3489's plain `MAPPED-ADDRESS`; XOR-ing with the fixed magic cookie `0x2112A442` hides the value. The answer holds for that one server: on its own it does not show whether the NAT reuses the mapping toward other destinations.

code

pseudocode · 9 lines
pseudocode
MAGIC_COOKIE = 0x2112A442

function decode_xor_mapped_ipv4(x_port, x_address):
    port    = x_port XOR (MAGIC_COOKIE >> 16)    # 0xD322 XOR 0x2112 = 0xF230 = 62000
    address = x_address XOR MAGIC_COOKIE         # 234.18.213.72 -> 203.0.113.10
    return (address, port)

# IPv6: XOR the 128-bit address with
#       MAGIC_COOKIE followed by the 96-bit transaction ID

go deeper

for a junior

Remember that the server simply reports the source address and port it saw, which is the NAT's public mapping, and that this is called the server-reflexive address.

for a middle

Walk a Binding request through the NAT, say why the answer travels in the payload rather than the headers, and explain the ALG problem that XOR-MAPPED-ADDRESS fixes.

for a senior

Stress the limits: the answer describes one mapping toward one server, reveals nothing about filtering, and only works for a peer if the NAT reuses the mapping across destinations.

for a principal

Weigh running your own STUN servers against relying on third parties: the answer is unauthenticated by default, so a wrong reflexive address is a correctness and an attack-surface question.

## What a Binding transaction is **STUN** (Session Traversal Utilities for NAT, RFC 8489) is a request/response protocol. Every message starts with a **20-byte header** carrying a message type (a *method* plus a *class*), a length, the fixed **magic cookie** `0x2112A442` and a random **96-bit transaction ID** that pairs a response with its request. RFC 8489 defines one method, **Binding**, used in two ways: - as a **request/response** transaction, to learn which mapping a NAT allocated to the client; - as a request or an **indication** (a message with no response), to keep that mapping alive by sending traffic through it. The default port is **3478** for STUN over UDP and TCP, and **5349** for STUN over TLS or DTLS. ## Walking one request through a NAT Take a host at `10.0.0.5:4000` behind a NAT whose public address is `203.0.113.10`, and a STUN server at `192.0.2.50:3478`. 1. The host sends a Binding request from `10.0.0.5:4000` to `192.0.2.50:3478`. 2. The NAT creates a mapping and rewrites the source to, say, `203.0.113.10:62000`. 3. The server sees `203.0.113.10:62000` as the source and copies exactly that into `XOR-MAPPED-ADDRESS` in a Binding success response. 4. The response returns to `203.0.113.10:62000`; the NAT rewrites the *destination in the headers* back to `10.0.0.5:4000`, but leaves the attribute inside the body untouched. 5. The host now knows that, toward this server, its packets appear as `203.0.113.10:62000`: its **server-reflexive** address. If several NATs are nested, each rewrites the source in turn, and the server reports the mapping of the **outermost** NAT, the one nearest the server. ## Why the address is XOR-encoded | Attribute | Encoding | Status | |---|---|---| | `MAPPED-ADDRESS` | address and port in plain binary | RFC 3489's original attribute, kept for backward compatibility | | `XOR-MAPPED-ADDRESS` | port XOR-ed with the top 16 bits of the magic cookie; an IPv4 address XOR-ed with the cookie; an IPv6 address XOR-ed with the cookie followed by the transaction ID | what a Binding response carries under RFC 8489 | RFC 8489 records why. Deployment found that some NATs scanned payloads for 32-bit values equal to their own public address and rewrote them, an attempt at a generic **Application Layer Gateway (ALG)**. That rewrote the very answer STUN was carrying and broke its message-integrity check as well. Once XOR-ed with a constant, the bytes no longer look like the address, so the ALG leaves them alone; the client reverses the XOR to read the value. It is **not encryption**: the cookie is public and anyone can decode the field. A worked example with the mapping above: the port `62000` (`0xF230`) XOR `0x2112` gives `0xD322`, which is `54050` on the wire; the address `203.0.113.10` XOR `0x2112A442` gives the bytes `234.18.213.72`. ## What the answer does and does not tell you - **It is valid for one destination.** It describes the mapping toward that server. A NAT with endpoint-independent mapping reuses it for every destination; one with endpoint-dependent mapping allocates a new public port for a new destination, so a peer sending to the learned address hits a port that is not mapped toward it. - **It says nothing about filtering.** Whether outside hosts other than the server may send to that port is the NAT's filtering behaviour, which one Binding exchange cannot reveal. - **It is not a NAT type.** RFC 3489, "classic STUN", tried to classify NATs into types from a sequence of tests. RFC 5389 obsoleted it, recording that the classification algorithm was faulty because many NATs did not fit the types, and that classic STUN could not tell whether a learned address would work. RFC 8489 continues that position. - **It is short-lived.** The mapping lasts only as long as the NAT's mapping timer; sending Binding requests or indications through it keeps it alive. ## Where STUN sits STUN is a tool that other procedures use. ICE (RFC 8445) uses the Binding response to gather server-reflexive candidates and reuses the Binding request itself as its connectivity check; TURN (RFC 8656) is an extension of STUN that adds relaying. A host that learns its reflexive address and simply advertises it, without testing, is back in the classic-STUN position that RFC 5389 abandoned.

  • Can a host tell from one STUN server whether its NAT uses endpoint-independent mapping?
    Not from one Binding exchange, which shows a single mapping toward a single server. Comparing the reflexive addresses that servers at two different IP addresses return hints at whether the NAT reuses the mapping, which is the kind of test RFC 3489 built its NAT-type classification on, and RFC 5389 recorded that classification as faulty. ICE does not classify at all; it tests the real paths.
  • Why must the host send its Binding request from the same local port it will use for data?
    A mapping is created per inside address and port. A request from a different local port creates a different mapping, so the reflexive address learned would describe a port the data never uses. ICE goes further and sends its connectivity checks from the exact addresses and ports the data will use.

saying these in an interview costs you the question

  • The STUN server reports the client's private address so the peer can reach it.
  • XOR-MAPPED-ADDRESS encrypts the mapped address so eavesdroppers cannot read it.
  • One STUN response proves any outside host can now send to that public port.
  • Behind nested NATs, STUN reports the mapping of the NAT nearest the client.
  • Classic RFC 3489 NAT-type detection is still the current way to choose a traversal strategy.