# `Ankusa.Net.ClientIP`
[🔗](https://github.com/jamescarr/ankusa/blob/ankusa-v0.7.0/packages/ankusa/lib/ankusa/net/client_ip.ex#L1)

Decide which address a request actually came from.

This is the piece an allowlist cannot afford to get wrong: `X-Forwarded-For`
is client-controlled, so reading it for a peer that is not itself a trusted
proxy is the same as letting the caller choose its own IP and walk straight
through the route guard.

The rule, in full:

  1. The peer is `conn.remote_ip`, normalized.
  2. No trusted proxies configured, or the peer matches none of them → the
     peer is the client. The header is not read at all.
  3. Otherwise the peer is a proxy, so the chain is trustworthy as far as this
     node can tell: every `x-forwarded-for` value is joined in the order the
     request carried them, split on commas and trimmed, and empty entries are
     dropped. Nothing left → the peer.
  4. The chain is walked right to left, one entry at a time and only as far as
     needed. Proxies append, so the rightmost entries are the ones this node's
     own proxies wrote, and the rightmost *untrusted* entry is the closest
     client we can vouch for: that entry is the client, and the entries to its
     left — the prefix a client can forge — are never even parsed. An entry
     that cannot be read is `:error`: a chain we cannot fully account for is
     not one we believe, and the request is denied rather than falling back to
     the proxy's own address, which is exactly what a forged entry would be
     trying to achieve. If every entry is trusted, the leftmost one is the
     client.
  5. An entry is a bare IPv4 or IPv6 address, or one of the `host:port` forms
     `1.2.3.4:5678` and `[2001:db8::1]:443` (the brackets are how an IPv6
     address, which owns colons itself, carries a port). Anything else —
     `garbage`, `1.2.3.4:abc`, `1.2.3.4:99999`, `[::1`, a non-UTF-8 byte — is
     an entry that cannot be read.

The result is normalized, so a peer or an entry written as the IPv4-mapped
`::ffff:1.2.3.4` resolves to an IPv4 address and matches IPv4 rules.

# `resolve`

```elixir
@spec resolve(Plug.Conn.t(), [Ankusa.Net.cidr()]) ::
  {:ok, :inet.ip_address()} | :error
```

Resolve the client address for `conn`, given the CIDRs whose peers may set
`X-Forwarded-For`.

`:error` when `conn.remote_ip` is nil — a conn with no peer at all (a test
conn with the field cleared) — or when an entry the chain depends on cannot be
read. Callers treat both as an IP rejection: there is no address to match
against rules, and neither case may quietly become the proxy's own address.

---

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