masque_chain_handler (masque v0.7.0)
View SourceMASQUE handler that chains to an upstream proxy.
Instead of opening a gen_udp / gen_tcp socket to the resolved target (what the built-in UDP / TCP / IP proxy handlers do), this handler opens a MASQUE client session to an upstream proxy and relays traffic both ways. The result is a two-hop tunnel:
Client -> Ingress (this handler) -> Egress (upstream) -> TargetThis is the server-side chaining pattern used by Apple Private Relay: the client connects to the Ingress; the Ingress chains to the Egress transparently.
Covers all three tunnel protocols:
- CONNECT-UDP (
protocol = udp): packets forwarded viamasque:send/2both ways. - CONNECT-TCP (
protocol = tcp): bytes forwarded viamasque:send/2both ways. - CONNECT-IP (
protocol = ip): IP packets forwarded viamasque:send_ip_packet/2; ROUTE_ADVERTISEMENT and unprompted ADDRESS_ASSIGN (request_id 0) from the upstream are forwarded to the client. Client-initiated ADDRESS_REQUEST forwarding with request-id mapping is not yet implemented; clients that need a round-trip address allocation through the chain have to wait for that follow-up.
Configure via handler_opts:
upstream_proxy := binary()- URI of the upstream proxy (e.g.<<"https://egress:4434">>). Required.upstream_opts => map()- options forwarded tomasque:connect/3for the upstream leg (verify, transports, timeout, etc.). Default#{verify => verify_none}.allow => fun(target()) -> boolean()- optional policy gate, same asmasque_udp_proxy_handler.
Summary
Functions
-spec accept(masque_handler:req()) -> masque_handler:accept_result().
-spec handle_capsule(non_neg_integer(), binary(), #state{upstream :: pid(), protocol :: udp | tcp | ip}) -> {ok, #state{upstream :: pid(), protocol :: udp | tcp | ip}}.
-spec init(masque_handler:req(), map()) -> {ok, #state{upstream :: pid(), protocol :: udp | tcp | ip}} | {stop, term()}.