masque

An Erlang implementation of the MASQUE family - RFC 9298 - Proxying UDP in HTTP, RFC 9484 - Proxying IP in HTTP, and the draft-ietf-httpbis-connect-tcp variant - over HTTP/3 and HTTP/2, built on erlang_quic (quic_h3) and erlang_h2.

The client races both transports Apple-style (h3 first, h2 after 250 ms, first 2xx wins) so tunnels connect quickly even on networks that block QUIC.

masque lets you tunnel arbitrary UDP flows (DNS, QUIC, WireGuard, game traffic, …), TCP streams, and full IP packets through an authenticated HTTPS endpoint. Both proxy server and client are shipped in this library.

Features

Installation

Add to your rebar.config:

{deps, [
{masque, {git, "https://github.com/benoitc/erlang_masque.git", {branch, "main"}}}
]}.

Quick start

Proxy server (serves both UDP and TCP tunnels)

{ok, _} = masque:start_listener(my_proxy, #{
port => 4433,
cert => CertDer,
key => KeyDer
%% One listener dispatches both connect-udp and connect-tcp.
%% Defaults: udp_handler = masque_udp_proxy_handler,
%% tcp_handler = masque_tcp_proxy_handler.
}).

Client

%% UDP tunnel (DNS, QUIC, game traffic)
{ok, Sess} = masque:connect(<<"https://proxy:4433">>,
{<<"1.1.1.1">>, 53},
#{protocol => udp}).
%% TCP tunnel (web, TLS)
{ok, Sess} = masque:connect(<<"https://proxy:4433">>,
{<<"example.com">>, 443},
#{protocol => tcp}).
%% Unified send/recv - works for both protocols
ok = masque:send(Sess, Data),
{ok, Reply} = masque:recv(Sess, 5000),
ok = masque:close(Sess).
%% Message mode (default): owner receives {masque_data, Sess, Data}
receive {masque_data, Sess, Bytes} -> Bytes end.

Custom server handler

-module(my_handler).
-behaviour(masque_handler).
-export([accept/1, init/2, handle_packet/2, handle_data/2, terminate/2]).
accept(#{target_host := Host}) ->
case is_allowed(Host) of
true -> accept;
false -> {reject, forbidden}
end.
init(_Req, _Opts) -> {ok, #{}}.
%% UDP tunnels
handle_packet(Data, S) -> {ok, S, [{send, Data}]}.
%% TCP tunnels
handle_data(Data, S) -> {ok, S, [{send_data, Data}]}.
terminate(_Reason, _S) -> ok.

Examples

Building and testing

rebar3 compile
rebar3 eunit # unit tests (codecs)
rebar3 as test proper # PropEr properties
rebar3 ct # common_test (17 cases)
rebar3 xref
rebar3 dialyzer

External-peer interop:

MASQUE_GO_BIN=/path/to/masque-go rebar3 ct --suite=masque_interop_SUITE

License

Apache License 2.0. See LICENSE.