anchor.diff:  	case Rest0 of
   		<<Subs:SubsLen/binary, Rest/bits>> ->
   			SSL0 = #{
  -				client => client(<<Client>>),
  +				client => parse_client(<<Client>>),
   				verified => Verify =:= 0
   			},
   			case parse_ssl_tlv(Subs, SubsLen, SSL0) of
  
positive.code: parse_v2(<<_:12, Protocol:4, _/bits>>) when Protocol > 2 ->
  	{error, 'Invalid transport protocol in the PROXY protocol binary header. (PP 2.2)'}.
  
  family(0) -> undefined;
  family(1) -> ipv4;
  family(2) -> ipv6;
  family(3) -> unix.
  
  protocol(0) -> undefined;
  protocol(1) -> stream;
  protocol(2) -> dgram.
  
  parse_v2(Data, Len, Family, Protocol, _)
  		when Family =:= undefined; Protocol =:= undefined ->
  
negative.code: ssl_subtype(16#24) -> sig_alg;
  ssl_subtype(16#25) -> key_alg;
  ssl_subtype(_) -> undefined.
  
  %% Building.
  
  -spec header(proxy_info()) -> iodata().
  header(ProxyInfo) ->
  	header(ProxyInfo, #{}).
  
  -spec header(proxy_info(), build_opts()) -> iodata().
  header(#{version := 2, command := local}, _) ->
  	<<"\r\n\r\n\0\r\nQUIT\n", 2:4, 0:28>>;
  header(#{version := 2, command := proxy,
  		transport_family := Family,
  		transport_protocol := Protocol}, _)
  		when Family =:= undefined; Protocol =:= undefined ->
  	<<"\r\n\r\n\0\r\nQUIT\n", 2:4, 1:4, 0:24>>;
  header(ProxyInfo=#{version := 2, command := proxy,
  		transport_family := Family,
  		transport_protocol := Protocol}, Opts) ->
  	Addresses = addresses(ProxyInfo),
  	TLVs = tlvs(ProxyInfo, Opts),
  	ExtraLen = case Opts of
  		#{checksum := crc32} -> 7;
  		_ -> 0
  	end,
  	Len = iolist_size(Addresses) + iolist_size(TLVs) + ExtraLen,
  	Header = [
  		<<"\r\n\r\n\0\r\nQUIT\n", 2:4, 1:4>>,
  		<<(family(Family)):4, (protocol(Protocol)):4>>,
  		<<Len:16>>,
  		Addresses,
  		TLVs
  	],
  	case Opts of
  		#{checksum := crc32} ->
  			CRC32C = erlang:crc32([Header, <<16#3, 4:16, 0:32>>]),
  			[Header, <<16#3, 4:16, CRC32C:32>>];
  		_ ->
  			Header
  	end;
  header(#{version := 1, command := proxy,
  		transport_family := undefined,
  		transport_protocol := undefined}, _) ->
  	<<"PROXY UNKNOWN\r\n">>;
  header(#{version := 1, command := proxy,
  		transport_family := Family0,
  		transport_protocol := stream,
  		src_address := SrcAddress, src_port := SrcPort,
  		dest_address := DestAddress, dest_port := DestPort}, _)
  		when SrcPort > 0, SrcPort =< 65535, DestPort > 0, DestPort =< 65535 ->
  	[
  		<<"PROXY ">>,
  		case Family0 of
  			ipv4 when tuple_size(SrcAddress) =:= 4, tuple_size(DestAddress) =:= 4 ->
  				[<<"TCP4 ">>, inet:ntoa(SrcAddress), $\s, inet:ntoa(DestAddress)];
  			ipv6 when tuple_size(SrcAddress) =:= 8, tuple_size(DestAddress) =:= 8 ->
  				[<<"TCP6 ">>, inet:ntoa(SrcAddress), $\s, inet:ntoa(DestAddress)]
  		end,
  		$\s,
  		integer_to_binary(SrcPort),
  		$\s,
  		integer_to_binary(DestPort),
  		$\r, $\n
  	].
  
  family(ipv4) -> 1;
  family(ipv6) -> 2;
  family(unix) -> 3.
  
  protocol(stream) -> 1;
  protocol(dgram) -> 2.
  
  addresses(#{transport_family := ipv4,
  		src_address := {S1, S2, S3, S4}, src_port := SrcPort,
  		dest_address := {D1, D2, D3, D4}, dest_port := DestPort})
  		when SrcPort > 0, SrcPort =< 65535, DestPort > 0, DestPort =< 65535 ->
  	<<S1, S2, S3, S4, D1, D2, D3, D4, SrcPort:16, DestPort:16>>;
  addresses(#{transport_family := ipv6,
  		src_address := {S1, S2, S3, S4, S5, S6, S7, S8}, src_port := SrcPort,
  		dest_address := {D1, D2, D3, D4, D5, D6, D7, D8}, dest_port := DestPort})
  		when SrcPort > 0, SrcPort =< 65535, DestPort > 0, DestPort =< 65535 ->
  	<<
  		S1:16, S2:16, S3:16, S4:16, S5:16, S6:16, S7:16, S8:16,
  		D1:16, D2:16, D3:16, D4:16, D5:16, D6:16, D7:16, D8:16,
  		SrcPort:16, DestPort:16
  	>>;
  addresses(#{transport_family := unix,
  		src_address := SrcAddress, dest_address := DestAddress})
  		when byte_size(SrcAddress) =< 108, byte_size(DestAddress) =< 108 ->
  	SrcPadding = 8 * (108 - byte_size(SrcAddress)),
  	DestPadding = 8 * (108 - byte_size(DestAddress)),
  	<<
  		SrcAddress/binary, 0:SrcPadding,
  		DestAddress/binary, 0:DestPadding
  	>>.
  
  tlvs(ProxyInfo, Opts) ->
  	[
  		binary_tlv(ProxyInfo, alpn, 16#1),
  		binary_tlv(ProxyInfo, authority, 16#2),
  		ssl_tlv(ProxyInfo),
  		binary_tlv(ProxyInfo, netns, 16#30),
  		raw_tlvs(ProxyInfo),
  		noop_tlv(Opts)
  	].
  
  binary_tlv(Info, Key, Type) ->
  	case Info of
  		#{Key := Bin} ->
  			Len = byte_size(Bin),
  			<<Type, Len:16, Bin/binary>>;
  		_ ->
  			<<>>
  	end.
  
  noop_tlv(#{padding := Len0}) when Len0 >= 3 ->
  	Len = Len0 - 3,
  	<<16#4, Len:16, 0:Len/unit:8>>;
  noop_tlv(_) ->
  	<<>>.
  
  ssl_tlv(#{ssl := Info=#{client := Client0, verified := Verify0}}) ->
  	Client = client(Client0, 0),
  	Verify = if
  		Verify0 -> 0;
  		not Verify0 -> 1
  	end,
  	TLVs = [
  		binary_tlv(Info, version, 16#21),
  		binary_tlv(Info, cn, 16#22),
  		binary_tlv(Info, cipher, 16#23),
  		binary_tlv(Info, sig_alg, 16#24),
  		binary_tlv(Info, key_alg, 16#25)
  	],
  	Len = iolist_size(TLVs) + 5,
  	[<<16#20, Len:16, Client, Verify:32>>, TLVs];
  ssl_tlv(_) ->
  	<<>>.
  
  client([], Client) -> Client;
  client([ssl|Tail], Client) -> client(Tail, Client bor 16#1);
  client([cert_conn|Tail], Client) -> client(Tail, Client bor 16#2);
  client([cert_sess|Tail], Client) -> client(Tail, Client bor 16#4).
  
  raw_tlvs(Info) ->
  	[begin
  		Len = byte_size(Bin),
  		<<Type, Len:16, Bin/binary>>
  	end || {Type, Bin} <- maps:get(raw_tlvs, Info, [])].
  
  -ifdef(TEST).
  v1_test() ->
  	Test1 = #{
  		version => 1,
  		command => proxy,
  		transport_family => undefined,
  		transport_protocol => undefined
  	},
  	{ok, Test1, <<>>} = parse(iolist_to_binary(header(Test1))),
  	Test2 = #{
  		version => 1,
  		command => proxy,
  		transport_family => ipv4,
  		transport_protocol => stream,
  		src_address => {127, 0, 0, 1},
  		src_port => 1234,
  		dest_address => {10, 11, 12, 13},
  		dest_port => 23456
  	},
  	{ok, Test2, <<>>} = parse(iolist_to_binary(header(Test2))),
  	Test3 = #{
  		version => 1,
  		command => proxy,
  		transport_family => ipv6,
  		transport_protocol => stream,
  		src_address => {1, 2, 3, 4, 5, 6, 7, 8},
  		src_port => 1234,
  		dest_address => {65535, 55555, 2222, 333, 1, 9999, 777, 8},
  		dest_port => 23456
  	},
  	{ok, Test3, <<>>} = parse(iolist_to_binary(header(Test3))),
  	ok.
  
  v2_test() ->
  	Test0 = #{
  		version => 2,
  		command => local
  	},
  	{ok, Test0, <<>>} = parse(iolist_to_binary(header(Test0))),
  	Test1 = #{
  		version => 2,
  		command => proxy,
  		transport_family => undefined,
  		transport_protocol => undefined
  	},
  	{ok, Test1, <<>>} = parse(iolist_to_binary(header(Test1))),
  	Test2 = #{
  		version => 2,
  		command => proxy,
  		transport_family => ipv4,
  		transport_protocol => stream,
  		src_address => {127, 0, 0, 1},
  		src_port => 1234,
  		dest_address => {10, 11, 12, 13},
  		dest_port => 23456
  	},
  	{ok, Test2, <<>>} = parse(iolist_to_binary(header(Test2))),
  	Test3 = #{
  		version => 2,
  		command => proxy,
  		transport_family => ipv6,
  		transport_protocol => stream,
  		src_address => {1, 2, 3, 4, 5, 6, 7, 8},
  		src_port => 1234,
  		dest_address => {65535, 55555, 2222, 333, 1, 9999, 777, 8},
  		dest_port => 23456
  	},
  	{ok, Test3, <<>>} = parse(iolist_to_binary(header(Test3))),
  	Test4 = #{
  		version => 2,
  		command => proxy,
  		transport_family => unix,
  		transport_protocol => dgram,
  		src_address => <<"/run/source.sock">>,
  		dest_address => <<"/run/destination.sock">>
  	},
  	{ok, Test4, <<>>} = parse(iolist_to_binary(header(Test4))),
  	ok.
  
  v2_tlvs_test() ->
  	Common = #{
  		version => 2,
  		command => proxy,
  		transport_family => ipv4,
  		transport_protocol => stream,
  		src_address => {127, 0, 0, 1},
  		src_port => 1234,
  		dest_address => {10, 11, 12, 13},
  		dest_port => 23456
  	},
  	Test1 = Common#{alpn => <<"h2">>},
  	{ok, Test1, <<>>} = parse(iolist_to_binary(header(Test1))),
  	Test2 = Common#{authority => <<"internal.example.org">>},
  	{ok, Test2, <<>>} = parse(iolist_to_binary(header(Test2))),
  	Test3 = Common#{netns => <<"/var/run/netns/example">>},
  	{ok, Test3, <<>>} = parse(iolist_to_binary(header(Test3))),
  	Test4 = Common#{ssl => #{
  		client => [ssl, cert_conn, cert_sess],
  		verified => true,
  		version => <<"TLSv1.3">>, %% Note that I'm not sure this example value is correct.
  		cipher => <<"ECDHE-RSA-AES128-GCM-SHA256">>,
  		sig_alg => <<"SHA256">>,
  		key_alg => <<"RSA2048">>,
  		cn => <<"example.com">>
  	}},
  	{ok, Test4, <<>>} = parse(iolist_to_binary(header(Test4))),
  	%% Note that the raw_tlvs order is not relevant and therefore
  	%% the parser does not reverse the list it builds.
  	Test5In = Common#{raw_tlvs => RawTLVs=[
  		%% The only custom TLV I am aware of is defined at:
  		%% https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol
  		{16#ea, <<16#1, "instance-id">>},
  		%% This TLV is entirely fictional.
  		{16#ff, <<1, 2, 3, 4, 5, 6, 7, 8, 9, 0>>}
  	]},
  	Test5Out = Test5In#{raw_tlvs => lists:reverse(RawTLVs)},
  	{ok, Test5Out, <<>>} = parse(iolist_to_binary(header(Test5In))),
  	ok.
  
  v2_checksum_test() ->
  	Test = #{
  		version => 2,
  		command => proxy,
  		transport_family => ipv4,
  		transport_protocol => stream,
  		src_address => {127, 0, 0, 1},
  		src_port => 1234,
  		dest_address => {10, 11, 12, 13},
  		dest_port => 23456
  	},
  	{ok, Test, <<>>} = parse(iolist_to_binary(header(Test, #{checksum => crc32}))),
  	ok.
  
  v2_padding_test() ->
  	Test = #{
  		version => 2,
  		command => proxy,
  		transport_family => ipv4,
  		transport_protocol => stream,
  		src_address => {127, 0, 0, 1},
  		src_port => 1234,
  		dest_address => {10, 11, 12, 13},
  		dest_port => 23456
  	},
  	{ok, Test, <<>>} = parse(iolist_to_binary(header(Test, #{padding => 123}))),
  	ok.
  -endif.
  