zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

ranch_proxy_header.erl (28070B)


      1 %% Copyright (c) 2018, Loïc Hoguin <essen@ninenines.eu>
      2 %%
      3 %% Permission to use, copy, modify, and/or distribute this software for any
      4 %% purpose with or without fee is hereby granted, provided that the above
      5 %% copyright notice and this permission notice appear in all copies.
      6 %%
      7 %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      8 %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      9 %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     10 %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     11 %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     12 %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     13 %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     14 
     15 -module(ranch_proxy_header).
     16 
     17 -export([parse/1]).
     18 -export([header/1]).
     19 -export([header/2]).
     20 
     21 -type proxy_info() :: #{
     22 	%% Mandatory part.
     23 	version := 1 | 2,
     24 	command := local | proxy,
     25 	transport_family => undefined | ipv4 | ipv6 | unix,
     26 	transport_protocol => undefined | stream | dgram,
     27 	%% Addresses.
     28 	src_address => inet:ip_address() | binary(),
     29 	src_port => inet:port_number(),
     30 	dest_address => inet:ip_address() | binary(),
     31 	dest_port => inet:port_number(),
     32 	%% Extra TLV-encoded data.
     33 	alpn => binary(), %% US-ASCII.
     34 	authority => binary(), %% UTF-8.
     35 	ssl => #{
     36 		client := [ssl | cert_conn | cert_sess],
     37 		verified := boolean(),
     38 		version => binary(), %% US-ASCII.
     39 		cipher => binary(), %% US-ASCII.
     40 		sig_alg => binary(), %% US-ASCII.
     41 		key_alg => binary(), %% US-ASCII.
     42 		cn => binary() %% UTF-8.
     43 	},
     44 	netns => binary(), %% US-ASCII.
     45 	%% Unknown TLVs can't be parsed so the raw data is given.
     46 	raw_tlvs => [{0..255, binary()}]
     47 }.
     48 -export_type([proxy_info/0]).
     49 
     50 -type build_opts() :: #{
     51 	checksum => crc32c,
     52 	padding => pos_integer() %% >= 3
     53 }.
     54 
     55 %% Parsing.
     56 
     57 -spec parse(Data) -> {ok, proxy_info(), Data} | {error, atom()} when Data::binary().
     58 parse(<<"\r\n\r\n\0\r\nQUIT\n", Rest/bits>>) ->
     59 	parse_v2(Rest);
     60 parse(<<"PROXY ", Rest/bits>>) ->
     61 	parse_v1(Rest);
     62 parse(_) ->
     63 	{error, 'The PROXY protocol header signature was not recognized. (PP 2.1, PP 2.2)'}.
     64 
     65 -ifdef(TEST).
     66 parse_unrecognized_header_test() ->
     67 	{error, _} = parse(<<"GET / HTTP/1.1\r\n">>),
     68 	ok.
     69 -endif.
     70 
     71 %% Human-readable header format (Version 1).
     72 parse_v1(<<"TCP4 ", Rest/bits>>) ->
     73 	parse_v1(Rest, ipv4);
     74 parse_v1(<<"TCP6 ", Rest/bits>>) ->
     75 	parse_v1(Rest, ipv6);
     76 parse_v1(<<"UNKNOWN\r\n", Rest/bits>>) ->
     77 	{ok, #{
     78 		version => 1,
     79 		command => proxy,
     80 		transport_family => undefined,
     81 		transport_protocol => undefined
     82 	}, Rest};
     83 parse_v1(<<"UNKNOWN ", Rest0/bits>>) ->
     84 	case binary:split(Rest0, <<"\r\n">>) of
     85 		[_, Rest] ->
     86 			{ok, #{
     87 				version => 1,
     88 				command => proxy,
     89 				transport_family => undefined,
     90 				transport_protocol => undefined
     91 			}, Rest};
     92 		[_] ->
     93 			{error, 'Malformed or incomplete PROXY protocol header line. (PP 2.1)'}
     94 	end;
     95 parse_v1(_) ->
     96 	{error, 'The INET protocol and family string was not recognized. (PP 2.1)'}.
     97 
     98 parse_v1(Rest0, Family) ->
     99 	try
    100 		{ok, SrcAddr, Rest1} = parse_ip(Rest0, Family),
    101 		{ok, DestAddr, Rest2} = parse_ip(Rest1, Family),
    102 		{ok, SrcPort, Rest3} = parse_port(Rest2, $\s),
    103 		{ok, DestPort, Rest4} = parse_port(Rest3, $\r),
    104 		<<"\n", Rest/bits>> = Rest4,
    105 		{ok, #{
    106 			version => 1,
    107 			command => proxy,
    108 			transport_family => Family,
    109 			transport_protocol => stream,
    110 			src_address => SrcAddr,
    111 			src_port => SrcPort,
    112 			dest_address => DestAddr,
    113 			dest_port => DestPort
    114 		}, Rest}
    115 	catch
    116 		throw:parse_ipv4_error ->
    117 			{error, 'Failed to parse an IPv4 address in the PROXY protocol header line. (PP 2.1)'};
    118 		throw:parse_ipv6_error ->
    119 			{error, 'Failed to parse an IPv6 address in the PROXY protocol header line. (PP 2.1)'};
    120 		throw:parse_port_error ->
    121 			{error, 'Failed to parse a port number in the PROXY protocol header line. (PP 2.1)'};
    122 		_:_ ->
    123 			{error, 'Malformed or incomplete PROXY protocol header line. (PP 2.1)'}
    124 	end.
    125 
    126 parse_ip(<<Addr:7/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest);
    127 parse_ip(<<Addr:8/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest);
    128 parse_ip(<<Addr:9/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest);
    129 parse_ip(<<Addr:10/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest);
    130 parse_ip(<<Addr:11/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest);
    131 parse_ip(<<Addr:12/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest);
    132 parse_ip(<<Addr:13/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest);
    133 parse_ip(<<Addr:14/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest);
    134 parse_ip(<<Addr:15/binary, $\s, Rest/binary>>, ipv4) -> parse_ipv4(Addr, Rest);
    135 parse_ip(Data, ipv6) ->
    136 	[Addr, Rest] = binary:split(Data, <<$\s>>),
    137 	parse_ipv6(Addr, Rest).
    138 
    139 parse_ipv4(Addr0, Rest) ->
    140 	case inet:parse_ipv4strict_address(binary_to_list(Addr0)) of
    141 		{ok, Addr} -> {ok, Addr, Rest};
    142 		{error, einval} -> throw(parse_ipv4_error)
    143 	end.
    144 
    145 parse_ipv6(Addr0, Rest) ->
    146 	case inet:parse_ipv6strict_address(binary_to_list(Addr0)) of
    147 		{ok, Addr} -> {ok, Addr, Rest};
    148 		{error, einval} -> throw(parse_ipv6_error)
    149 	end.
    150 
    151 parse_port(<<Port:1/binary, C, Rest/bits>>, C) -> parse_port(Port, Rest);
    152 parse_port(<<Port:2/binary, C, Rest/bits>>, C) -> parse_port(Port, Rest);
    153 parse_port(<<Port:3/binary, C, Rest/bits>>, C) -> parse_port(Port, Rest);
    154 parse_port(<<Port:4/binary, C, Rest/bits>>, C) -> parse_port(Port, Rest);
    155 parse_port(<<Port:5/binary, C, Rest/bits>>, C) -> parse_port(Port, Rest);
    156 
    157 parse_port(Port0, Rest) ->
    158 	try binary_to_integer(Port0) of
    159 		Port when Port > 0, Port =< 65535 ->
    160 			{ok, Port, Rest};
    161 		_ ->
    162 			throw(parse_port_error)
    163 	catch _:_ ->
    164 		throw(parse_port_error)
    165 	end.
    166 
    167 -ifdef(TEST).
    168 parse_v1_test() ->
    169 	%% Examples taken from the PROXY protocol header specification.
    170 	{ok, #{
    171 		version := 1,
    172 		command := proxy,
    173 		transport_family := ipv4,
    174 		transport_protocol := stream,
    175 		src_address := {255, 255, 255, 255},
    176 		src_port := 65535,
    177 		dest_address := {255, 255, 255, 255},
    178 		dest_port := 65535
    179 	}, <<>>} = parse(<<"PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n">>),
    180 	{ok, #{
    181 		version := 1,
    182 		command := proxy,
    183 		transport_family := ipv6,
    184 		transport_protocol := stream,
    185 		src_address := {65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535},
    186 		src_port := 65535,
    187 		dest_address := {65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535},
    188 		dest_port := 65535
    189 	}, <<>>} = parse(<<"PROXY TCP6 "
    190 		"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff "
    191 		"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n">>),
    192 	{ok, #{
    193 		version := 1,
    194 		command := proxy,
    195 		transport_family := undefined,
    196 		transport_protocol := undefined
    197 	}, <<>>} = parse(<<"PROXY UNKNOWN\r\n">>),
    198 	{ok, #{
    199 		version := 1,
    200 		command := proxy,
    201 		transport_family := undefined,
    202 		transport_protocol := undefined
    203 	}, <<>>} = parse(<<"PROXY UNKNOWN "
    204 		"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff "
    205 		"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n">>),
    206 	{ok, #{
    207 		version := 1,
    208 		command := proxy,
    209 		transport_family := ipv4,
    210 		transport_protocol := stream,
    211 		src_address := {192, 168, 0, 1},
    212 		src_port := 56324,
    213 		dest_address := {192, 168, 0, 11},
    214 		dest_port := 443
    215 	}, <<"GET / HTTP/1.1\r\nHost: 192.168.0.11\r\n\r\n">>} = parse(<<
    216 		"PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\n"
    217 		"GET / HTTP/1.1\r\n"
    218 		"Host: 192.168.0.11\r\n"
    219 		"\r\n">>),
    220 	%% Test cases taken from tomciopp/proxy_protocol.
    221 	{ok, #{
    222 		version := 1,
    223 		command := proxy,
    224 		transport_family := ipv4,
    225 		transport_protocol := stream,
    226 		src_address := {192, 168, 0, 1},
    227 		src_port := 56324,
    228 		dest_address := {192, 168, 0, 11},
    229 		dest_port := 443
    230 	}, <<"GET / HTTP/1.1\r">>} = parse(<<
    231 		"PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\nGET / HTTP/1.1\r">>),
    232 	{error, _} = parse(<<"PROXY TCP4 192.1638.0.1 192.168.0.11 56324 443\r\nGET / HTTP/1.1\r">>),
    233 	{error, _} = parse(<<"PROXY TCP4 192.168.0.1 192.168.0.11 1111111 443\r\nGET / HTTP/1.1\r">>),
    234 	{ok, #{
    235 		version := 1,
    236 		command := proxy,
    237 		transport_family := ipv6,
    238 		transport_protocol := stream,
    239 		src_address := {8193, 3512, 0, 66, 0, 35374, 880, 29492},
    240 		src_port := 4124,
    241 		dest_address := {8193, 3512, 0, 66, 0, 35374, 880, 29493},
    242 		dest_port := 443
    243 	}, <<"GET / HTTP/1.1\r">>} = parse(<<"PROXY TCP6 "
    244 		"2001:0db8:0000:0042:0000:8a2e:0370:7334 "
    245 		"2001:0db8:0000:0042:0000:8a2e:0370:7335 4124 443\r\nGET / HTTP/1.1\r">>),
    246 	{error, _} = parse(<<"PROXY TCP6 "
    247 		"2001:0db8:0000:0042:0000:8a2e:0370:7334 "
    248 		"2001:0db8:00;0:0042:0000:8a2e:0370:7335 4124 443\r\nGET / HTTP/1.1\r">>),
    249 	{error, _} = parse(<<"PROXY TCP6 "
    250 		"2001:0db8:0000:0042:0000:8a2e:0370:7334 "
    251 		"2001:0db8:0000:0042:0000:8a2e:0370:7335 4124 foo\r\nGET / HTTP/1.1\r">>),
    252 	{ok, #{
    253 		version := 1,
    254 		command := proxy,
    255 		transport_family := undefined,
    256 		transport_protocol := undefined
    257 	}, <<"GET / HTTP/1.1\r">>} = parse(<<"PROXY UNKNOWN 4124 443\r\nGET / HTTP/1.1\r">>),
    258 	{ok, #{
    259 		version := 1,
    260 		command := proxy,
    261 		transport_family := undefined,
    262 		transport_protocol := undefined
    263 	}, <<"GET / HTTP/1.1\r">>} = parse(<<"PROXY UNKNOWN\r\nGET / HTTP/1.1\r">>),
    264 	ok.
    265 -endif.
    266 
    267 %% Binary header format (version 2).
    268 
    269 %% LOCAL.
    270 parse_v2(<<2:4, 0:4, _:8, Len:16, Rest0/bits>>) ->
    271 	case Rest0 of
    272 		<<_:Len/binary, Rest/bits>> ->
    273 			{ok, #{
    274 				version => 2,
    275 				command => local
    276 			}, Rest};
    277 		_ ->
    278 			{error, 'Missing data in the PROXY protocol binary header. (PP 2.2)'}
    279 	end;
    280 %% PROXY.
    281 parse_v2(<<2:4, 1:4, Family:4, Protocol:4, Len:16, Rest/bits>>)
    282 		when Family =< 3, Protocol =< 2 ->
    283 	case Rest of
    284 		<<Header:Len/binary, _/bits>> ->
    285 			parse_v2(Rest, Len, parse_family(Family), parse_protocol(Protocol),
    286 				<<Family:4, Protocol:4, Len:16, Header:Len/binary>>);
    287 		_ ->
    288 			{error, 'Missing data in the PROXY protocol binary header. (PP 2.2)'}
    289 	end;
    290 %% Errors.
    291 parse_v2(<<Version:4, _/bits>>) when Version =/= 2 ->
    292 	{error, 'Invalid version in the PROXY protocol binary header. (PP 2.2)'};
    293 parse_v2(<<_:4, Command:4, _/bits>>) when Command > 1 ->
    294 	{error, 'Invalid command in the PROXY protocol binary header. (PP 2.2)'};
    295 parse_v2(<<_:8, Family:4, _/bits>>) when Family > 3 ->
    296 	{error, 'Invalid address family in the PROXY protocol binary header. (PP 2.2)'};
    297 parse_v2(<<_:12, Protocol:4, _/bits>>) when Protocol > 2 ->
    298 	{error, 'Invalid transport protocol in the PROXY protocol binary header. (PP 2.2)'}.
    299 
    300 parse_family(0) -> undefined;
    301 parse_family(1) -> ipv4;
    302 parse_family(2) -> ipv6;
    303 parse_family(3) -> unix.
    304 
    305 parse_protocol(0) -> undefined;
    306 parse_protocol(1) -> stream;
    307 parse_protocol(2) -> dgram.
    308 
    309 parse_v2(Data, Len, Family, Protocol, _)
    310 		when Family =:= undefined; Protocol =:= undefined ->
    311 	<<_:Len/binary, Rest/bits>> = Data,
    312 	{ok, #{
    313 		version => 2,
    314 		command => proxy,
    315 		%% In case only one value was undefined, we set both explicitly.
    316 		%% It doesn't make sense to have only one known value.
    317 		transport_family => undefined,
    318 		transport_protocol => undefined
    319 	}, Rest};
    320 parse_v2(<<
    321 		S1, S2, S3, S4,
    322 		D1, D2, D3, D4,
    323 		SrcPort:16, DestPort:16, Rest/bits>>, Len, Family=ipv4, Protocol, Header)
    324 		when Len >= 12 ->
    325 	parse_tlv(Rest, Len - 12, #{
    326 		version => 2,
    327 		command => proxy,
    328 		transport_family => Family,
    329 		transport_protocol => Protocol,
    330 		src_address => {S1, S2, S3, S4},
    331 		src_port => SrcPort,
    332 		dest_address => {D1, D2, D3, D4},
    333 		dest_port => DestPort
    334 	}, Header);
    335 parse_v2(<<
    336 		S1:16, S2:16, S3:16, S4:16, S5:16, S6:16, S7:16, S8:16,
    337 		D1:16, D2:16, D3:16, D4:16, D5:16, D6:16, D7:16, D8:16,
    338 		SrcPort:16, DestPort:16, Rest/bits>>, Len, Family=ipv6, Protocol, Header)
    339 		when Len >= 36 ->
    340 	parse_tlv(Rest, Len - 36, #{
    341 		version => 2,
    342 		command => proxy,
    343 		transport_family => Family,
    344 		transport_protocol => Protocol,
    345 		src_address => {S1, S2, S3, S4, S5, S6, S7, S8},
    346 		src_port => SrcPort,
    347 		dest_address => {D1, D2, D3, D4, D5, D6, D7, D8},
    348 		dest_port => DestPort
    349 	}, Header);
    350 parse_v2(<<SrcAddr0:108/binary, DestAddr0:108/binary, Rest/bits>>,
    351 		Len, Family=unix, Protocol, Header)
    352 		when Len >= 216 ->
    353 	try
    354 		[SrcAddr, _] = binary:split(SrcAddr0, <<0>>),
    355 		true = byte_size(SrcAddr) > 0,
    356 		[DestAddr, _] = binary:split(DestAddr0, <<0>>),
    357 		true = byte_size(DestAddr) > 0,
    358 		parse_tlv(Rest, Len - 216, #{
    359 			version => 2,
    360 			command => proxy,
    361 			transport_family => Family,
    362 			transport_protocol => Protocol,
    363 			src_address => SrcAddr,
    364 			dest_address => DestAddr
    365 		}, Header)
    366 	catch _:_ ->
    367 		{error, 'Invalid UNIX address in PROXY protocol binary header. (PP 2.2)'}
    368 	end;
    369 parse_v2(_, _, _, _, _) ->
    370 	{error, 'Invalid length in the PROXY protocol binary header. (PP 2.2)'}.
    371 
    372 -ifdef(TEST).
    373 parse_v2_test() ->
    374 	%% Test cases taken from tomciopp/proxy_protocol.
    375 	{ok, #{
    376 		version := 2,
    377 		command := proxy,
    378 		transport_family := ipv4,
    379 		transport_protocol := stream,
    380 		src_address := {127, 0, 0, 1},
    381 		src_port := 444,
    382 		dest_address := {192, 168, 0, 1},
    383 		dest_port := 443
    384 	}, <<"GET / HTTP/1.1\r\n">>} = parse(<<
    385 		13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, %% Signature.
    386 		33, %% Version and command.
    387 		17, %% Family and protocol.
    388 		0, 12, %% Length.
    389 		127, 0, 0, 1, %% Source address.
    390 		192, 168, 0, 1, %% Destination address.
    391 		1, 188, %% Source port.
    392 		1, 187, %% Destination port.
    393 		"GET / HTTP/1.1\r\n">>),
    394 	{ok, #{
    395 		version := 2,
    396 		command := proxy,
    397 		transport_family := ipv4,
    398 		transport_protocol := dgram,
    399 		src_address := {127, 0, 0, 1},
    400 		src_port := 444,
    401 		dest_address := {192, 168, 0, 1},
    402 		dest_port := 443
    403 	}, <<"GET / HTTP/1.1\r\n">>} = parse(<<
    404 		13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, %% Signature.
    405 		33, %% Version and command.
    406 		18, %% Family and protocol.
    407 		0, 12, %% Length.
    408 		127, 0, 0, 1, %% Source address.
    409 		192, 168, 0, 1, %% Destination address.
    410 		1, 188, %% Source port.
    411 		1, 187, %% Destination port.
    412 		"GET / HTTP/1.1\r\n">>),
    413 	{ok, #{
    414 		version := 2,
    415 		command := proxy,
    416 		transport_family := ipv6,
    417 		transport_protocol := stream,
    418 		src_address := {5532, 4240, 1, 0, 0, 0, 0, 0},
    419 		src_port := 444,
    420 		dest_address := {8193, 3512, 1, 0, 0, 0, 0, 0},
    421 		dest_port := 443
    422 	}, <<"GET / HTTP/1.1\r\n">>} = parse(<<
    423 		13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, %% Signature.
    424 		33, %% Version and command.
    425 		33, %% Family and protocol.
    426 		0, 36, %% Length.
    427 		21, 156, 16, 144, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, %% Source address.
    428 		32, 1, 13, 184, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, %% Destination address.
    429 		1, 188, %% Source port.
    430 		1, 187, %% Destination port.
    431 		"GET / HTTP/1.1\r\n">>),
    432 	{ok, #{
    433 		version := 2,
    434 		command := proxy,
    435 		transport_family := ipv6,
    436 		transport_protocol := dgram,
    437 		src_address := {5532, 4240, 1, 0, 0, 0, 0, 0},
    438 		src_port := 444,
    439 		dest_address := {8193, 3512, 1, 0, 0, 0, 0, 0},
    440 		dest_port := 443
    441 	}, <<"GET / HTTP/1.1\r\n">>} = parse(<<
    442 		13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, %% Signature.
    443 		33, %% Version and command.
    444 		34, %% Family and protocol.
    445 		0, 36, %% Length.
    446 		21, 156, 16, 144, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, %% Source address.
    447 		32, 1, 13, 184, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, %% Destination address.
    448 		1, 188, %% Source port.
    449 		1, 187, %% Destination port.
    450 		"GET / HTTP/1.1\r\n">>),
    451 	Path = <<"/var/pgsql_sock">>,
    452 	Len = byte_size(Path),
    453 	Padding = 8 * (108 - Len),
    454 	{ok, #{
    455 		version := 2,
    456 		command := proxy,
    457 		transport_family := unix,
    458 		transport_protocol := stream,
    459 		src_address := Path,
    460 		dest_address := Path
    461 	}, <<"GET / HTTP/1.1\r\n">>} = parse(<<
    462 		13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10,
    463 		33,
    464 		49,
    465 		0, 216,
    466 		Path/binary, 0:Padding,
    467 		Path/binary, 0:Padding,
    468 		"GET / HTTP/1.1\r\n">>),
    469 	{ok, #{
    470 		version := 2,
    471 		command := proxy,
    472 		transport_family := unix,
    473 		transport_protocol := dgram,
    474 		src_address := Path,
    475 		dest_address := Path
    476 	}, <<"GET / HTTP/1.1\r\n">>} = parse(<<
    477 		13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10,
    478 		33,
    479 		50,
    480 		0, 216,
    481 		Path/binary, 0:Padding,
    482 		Path/binary, 0:Padding,
    483 		"GET / HTTP/1.1\r\n">>),
    484 	ok.
    485 
    486 parse_v2_regression_test() ->
    487 	%% Real packet received from AWS. We confirm that the CRC32C
    488 	%% check succeeds only (in other words that ok is returned).
    489 	{ok, _, <<>>} = parse(<<
    490 		13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, 33, 17, 0, 84,
    491 		172, 31, 7, 113, 172, 31, 10, 31, 200, 242, 0, 80, 3, 0, 4,
    492 		232, 214, 137, 45, 234, 0, 23, 1, 118, 112, 99, 101, 45, 48,
    493 		56, 100, 50, 98, 102, 49, 53, 102, 97, 99, 53, 48, 48, 49, 99,
    494 		57, 4, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    495 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>),
    496 	ok.
    497 -endif.
    498 
    499 parse_tlv(Rest, 0, Info, _) ->
    500 	{ok, Info, Rest};
    501 %% PP2_TYPE_ALPN.
    502 parse_tlv(<<16#1, TLVLen:16, ALPN:TLVLen/binary, Rest/bits>>, Len, Info, Header) ->
    503 	parse_tlv(Rest, Len - TLVLen - 3, Info#{alpn => ALPN}, Header);
    504 %% PP2_TYPE_AUTHORITY.
    505 parse_tlv(<<16#2, TLVLen:16, Authority:TLVLen/binary, Rest/bits>>, Len, Info, Header) ->
    506 	parse_tlv(Rest, Len - TLVLen - 3, Info#{authority => Authority}, Header);
    507 %% PP2_TYPE_CRC32C.
    508 parse_tlv(<<16#3, TLVLen:16, CRC32C:32, Rest/bits>>, Len0, Info, Header) when TLVLen =:= 4 ->
    509 	Len = Len0 - TLVLen - 3,
    510 	BeforeLen = byte_size(Header) - Len - TLVLen,
    511 	<<Before:BeforeLen/binary, _:32, After:Len/binary>> = Header,
    512 	%% The initial CRC is ranch_crc32c:crc32c(<<"\r\n\r\n\0\r\nQUIT\n", 2:4, 1:4>>).
    513 	case ranch_crc32c:crc32c(2900412422, [Before, <<0:32>>, After]) of
    514 		CRC32C ->
    515 			parse_tlv(Rest, Len, Info, Header);
    516 		_ ->
    517 			{error, 'Failed CRC32C verification in PROXY protocol binary header. (PP 2.2)'}
    518 	end;
    519 %% PP2_TYPE_NOOP.
    520 parse_tlv(<<16#4, TLVLen:16, _:TLVLen/binary, Rest/bits>>, Len, Info, Header) ->
    521 	parse_tlv(Rest, Len - TLVLen - 3, Info, Header);
    522 %% PP2_TYPE_SSL.
    523 parse_tlv(<<16#20, TLVLen:16, Client, Verify:32, Rest0/bits>>, Len, Info, Header) ->
    524 	SubsLen = TLVLen - 5,
    525 	case Rest0 of
    526 		<<Subs:SubsLen/binary, Rest/bits>> ->
    527 			SSL0 = #{
    528 				client => parse_client(<<Client>>),
    529 				verified => Verify =:= 0
    530 			},
    531 			case parse_ssl_tlv(Subs, SubsLen, SSL0) of
    532 				{ok, SSL, <<>>} ->
    533 					parse_tlv(Rest, Len - TLVLen - 3, Info#{ssl => SSL}, Header);
    534 				Error={error, _} ->
    535 					Error
    536 			end;
    537 		_ ->
    538 			{error, 'Invalid TLV length in the PROXY protocol binary header. (PP 2.2)'}
    539 	end;
    540 %% PP2_TYPE_NETNS.
    541 parse_tlv(<<16#30, TLVLen:16, NetNS:TLVLen/binary, Rest/bits>>, Len, Info, Header) ->
    542 	parse_tlv(Rest, Len - TLVLen - 3, Info#{netns => NetNS}, Header);
    543 %% Unknown TLV.
    544 parse_tlv(<<TLVType, TLVLen:16, TLVValue:TLVLen/binary, Rest/bits>>, Len, Info, Header) ->
    545 	RawTLVs = maps:get(raw_tlvs, Info, []),
    546 	parse_tlv(Rest, Len - TLVLen - 3, Info#{raw_tlvs => [{TLVType, TLVValue}|RawTLVs]}, Header);
    547 %% Invalid TLV length.
    548 parse_tlv(_, _, _, _) ->
    549 	{error, 'Invalid TLV length in the PROXY protocol binary header. (PP 2.2)'}.
    550 
    551 parse_client(<<_:5, ClientCertSess:1, ClientCertConn:1, ClientSSL:1>>) ->
    552 	Client0 = case ClientCertSess of
    553 		0 -> [];
    554 		1 -> [cert_sess]
    555 	end,
    556 	Client1 = case ClientCertConn of
    557 		0 -> Client0;
    558 		1 -> [cert_conn|Client0]
    559 	end,
    560 	case ClientSSL of
    561 		0 -> Client1;
    562 		1 -> [ssl|Client1]
    563 	end.
    564 
    565 parse_ssl_tlv(Rest, 0, Info) ->
    566 	{ok, Info, Rest};
    567 %% Valid TLVs.
    568 parse_ssl_tlv(<<TLVType, TLVLen:16, TLVValue:TLVLen/binary, Rest/bits>>, Len, Info) ->
    569 	case ssl_subtype(TLVType) of
    570 		undefined ->
    571 			{error, 'Invalid TLV subtype for PP2_TYPE_SSL in PROXY protocol binary header. (PP 2.2)'};
    572 		Type ->
    573 			parse_ssl_tlv(Rest, Len - TLVLen - 3, Info#{Type => TLVValue})
    574 	end;
    575 %% Invalid TLV length.
    576 parse_ssl_tlv(_, _, _) ->
    577 	{error, 'Invalid TLV length in the PROXY protocol binary header. (PP 2.2)'}.
    578 
    579 ssl_subtype(16#21) -> version;
    580 ssl_subtype(16#22) -> cn;
    581 ssl_subtype(16#23) -> cipher;
    582 ssl_subtype(16#24) -> sig_alg;
    583 ssl_subtype(16#25) -> key_alg;
    584 ssl_subtype(_) -> undefined.
    585 
    586 %% Building.
    587 
    588 -spec header(proxy_info()) -> iodata().
    589 header(ProxyInfo) ->
    590 	header(ProxyInfo, #{}).
    591 
    592 -spec header(proxy_info(), build_opts()) -> iodata().
    593 header(#{version := 2, command := local}, _) ->
    594 	<<"\r\n\r\n\0\r\nQUIT\n", 2:4, 0:28>>;
    595 header(#{version := 2, command := proxy,
    596 		transport_family := Family,
    597 		transport_protocol := Protocol}, _)
    598 		when Family =:= undefined; Protocol =:= undefined ->
    599 	<<"\r\n\r\n\0\r\nQUIT\n", 2:4, 1:4, 0:24>>;
    600 header(ProxyInfo=#{version := 2, command := proxy,
    601 		transport_family := Family,
    602 		transport_protocol := Protocol}, Opts) ->
    603 	Addresses = addresses(ProxyInfo),
    604 	TLVs = tlvs(ProxyInfo, Opts),
    605 	ExtraLen = case Opts of
    606 		#{checksum := crc32c} -> 7;
    607 		_ -> 0
    608 	end,
    609 	Len = iolist_size(Addresses) + iolist_size(TLVs) + ExtraLen,
    610 	Header = [
    611 		<<"\r\n\r\n\0\r\nQUIT\n", 2:4, 1:4>>,
    612 		<<(family(Family)):4, (protocol(Protocol)):4>>,
    613 		<<Len:16>>,
    614 		Addresses,
    615 		TLVs
    616 	],
    617 	case Opts of
    618 		#{checksum := crc32c} ->
    619 			CRC32C = ranch_crc32c:crc32c([Header, <<16#3, 4:16, 0:32>>]),
    620 			[Header, <<16#3, 4:16, CRC32C:32>>];
    621 		_ ->
    622 			Header
    623 	end;
    624 header(#{version := 1, command := proxy,
    625 		transport_family := undefined,
    626 		transport_protocol := undefined}, _) ->
    627 	<<"PROXY UNKNOWN\r\n">>;
    628 header(#{version := 1, command := proxy,
    629 		transport_family := Family0,
    630 		transport_protocol := stream,
    631 		src_address := SrcAddress, src_port := SrcPort,
    632 		dest_address := DestAddress, dest_port := DestPort}, _)
    633 		when SrcPort > 0, SrcPort =< 65535, DestPort > 0, DestPort =< 65535 ->
    634 	[
    635 		<<"PROXY ">>,
    636 		case Family0 of
    637 			ipv4 when tuple_size(SrcAddress) =:= 4, tuple_size(DestAddress) =:= 4 ->
    638 				[<<"TCP4 ">>, inet:ntoa(SrcAddress), $\s, inet:ntoa(DestAddress)];
    639 			ipv6 when tuple_size(SrcAddress) =:= 8, tuple_size(DestAddress) =:= 8 ->
    640 				[<<"TCP6 ">>, inet:ntoa(SrcAddress), $\s, inet:ntoa(DestAddress)]
    641 		end,
    642 		$\s,
    643 		integer_to_binary(SrcPort),
    644 		$\s,
    645 		integer_to_binary(DestPort),
    646 		$\r, $\n
    647 	].
    648 
    649 family(ipv4) -> 1;
    650 family(ipv6) -> 2;
    651 family(unix) -> 3.
    652 
    653 protocol(stream) -> 1;
    654 protocol(dgram) -> 2.
    655 
    656 addresses(#{transport_family := ipv4,
    657 		src_address := {S1, S2, S3, S4}, src_port := SrcPort,
    658 		dest_address := {D1, D2, D3, D4}, dest_port := DestPort})
    659 		when SrcPort > 0, SrcPort =< 65535, DestPort > 0, DestPort =< 65535 ->
    660 	<<S1, S2, S3, S4, D1, D2, D3, D4, SrcPort:16, DestPort:16>>;
    661 addresses(#{transport_family := ipv6,
    662 		src_address := {S1, S2, S3, S4, S5, S6, S7, S8}, src_port := SrcPort,
    663 		dest_address := {D1, D2, D3, D4, D5, D6, D7, D8}, dest_port := DestPort})
    664 		when SrcPort > 0, SrcPort =< 65535, DestPort > 0, DestPort =< 65535 ->
    665 	<<
    666 		S1:16, S2:16, S3:16, S4:16, S5:16, S6:16, S7:16, S8:16,
    667 		D1:16, D2:16, D3:16, D4:16, D5:16, D6:16, D7:16, D8:16,
    668 		SrcPort:16, DestPort:16
    669 	>>;
    670 addresses(#{transport_family := unix,
    671 		src_address := SrcAddress, dest_address := DestAddress})
    672 		when byte_size(SrcAddress) =< 108, byte_size(DestAddress) =< 108 ->
    673 	SrcPadding = 8 * (108 - byte_size(SrcAddress)),
    674 	DestPadding = 8 * (108 - byte_size(DestAddress)),
    675 	<<
    676 		SrcAddress/binary, 0:SrcPadding,
    677 		DestAddress/binary, 0:DestPadding
    678 	>>.
    679 
    680 tlvs(ProxyInfo, Opts) ->
    681 	[
    682 		binary_tlv(ProxyInfo, alpn, 16#1),
    683 		binary_tlv(ProxyInfo, authority, 16#2),
    684 		ssl_tlv(ProxyInfo),
    685 		binary_tlv(ProxyInfo, netns, 16#30),
    686 		raw_tlvs(ProxyInfo),
    687 		noop_tlv(Opts)
    688 	].
    689 
    690 binary_tlv(Info, Key, Type) ->
    691 	case Info of
    692 		#{Key := Bin} ->
    693 			Len = byte_size(Bin),
    694 			<<Type, Len:16, Bin/binary>>;
    695 		_ ->
    696 			<<>>
    697 	end.
    698 
    699 noop_tlv(#{padding := Len0}) when Len0 >= 3 ->
    700 	Len = Len0 - 3,
    701 	<<16#4, Len:16, 0:Len/unit:8>>;
    702 noop_tlv(_) ->
    703 	<<>>.
    704 
    705 ssl_tlv(#{ssl := Info=#{client := Client0, verified := Verify0}}) ->
    706 	Client = client(Client0, 0),
    707 	Verify = if
    708 		Verify0 -> 0;
    709 		not Verify0 -> 1
    710 	end,
    711 	TLVs = [
    712 		binary_tlv(Info, version, 16#21),
    713 		binary_tlv(Info, cn, 16#22),
    714 		binary_tlv(Info, cipher, 16#23),
    715 		binary_tlv(Info, sig_alg, 16#24),
    716 		binary_tlv(Info, key_alg, 16#25)
    717 	],
    718 	Len = iolist_size(TLVs) + 5,
    719 	[<<16#20, Len:16, Client, Verify:32>>, TLVs];
    720 ssl_tlv(_) ->
    721 	<<>>.
    722 
    723 client([], Client) -> Client;
    724 client([ssl|Tail], Client) -> client(Tail, Client bor 16#1);
    725 client([cert_conn|Tail], Client) -> client(Tail, Client bor 16#2);
    726 client([cert_sess|Tail], Client) -> client(Tail, Client bor 16#4).
    727 
    728 raw_tlvs(Info) ->
    729 	[begin
    730 		Len = byte_size(Bin),
    731 		<<Type, Len:16, Bin/binary>>
    732 	end || {Type, Bin} <- maps:get(raw_tlvs, Info, [])].
    733 
    734 -ifdef(TEST).
    735 v1_test() ->
    736 	Test1 = #{
    737 		version => 1,
    738 		command => proxy,
    739 		transport_family => undefined,
    740 		transport_protocol => undefined
    741 	},
    742 	{ok, Test1, <<>>} = parse(iolist_to_binary(header(Test1))),
    743 	Test2 = #{
    744 		version => 1,
    745 		command => proxy,
    746 		transport_family => ipv4,
    747 		transport_protocol => stream,
    748 		src_address => {127, 0, 0, 1},
    749 		src_port => 1234,
    750 		dest_address => {10, 11, 12, 13},
    751 		dest_port => 23456
    752 	},
    753 	{ok, Test2, <<>>} = parse(iolist_to_binary(header(Test2))),
    754 	Test3 = #{
    755 		version => 1,
    756 		command => proxy,
    757 		transport_family => ipv6,
    758 		transport_protocol => stream,
    759 		src_address => {1, 2, 3, 4, 5, 6, 7, 8},
    760 		src_port => 1234,
    761 		dest_address => {65535, 55555, 2222, 333, 1, 9999, 777, 8},
    762 		dest_port => 23456
    763 	},
    764 	{ok, Test3, <<>>} = parse(iolist_to_binary(header(Test3))),
    765 	ok.
    766 
    767 v2_test() ->
    768 	Test0 = #{
    769 		version => 2,
    770 		command => local
    771 	},
    772 	{ok, Test0, <<>>} = parse(iolist_to_binary(header(Test0))),
    773 	Test1 = #{
    774 		version => 2,
    775 		command => proxy,
    776 		transport_family => undefined,
    777 		transport_protocol => undefined
    778 	},
    779 	{ok, Test1, <<>>} = parse(iolist_to_binary(header(Test1))),
    780 	Test2 = #{
    781 		version => 2,
    782 		command => proxy,
    783 		transport_family => ipv4,
    784 		transport_protocol => stream,
    785 		src_address => {127, 0, 0, 1},
    786 		src_port => 1234,
    787 		dest_address => {10, 11, 12, 13},
    788 		dest_port => 23456
    789 	},
    790 	{ok, Test2, <<>>} = parse(iolist_to_binary(header(Test2))),
    791 	Test3 = #{
    792 		version => 2,
    793 		command => proxy,
    794 		transport_family => ipv6,
    795 		transport_protocol => stream,
    796 		src_address => {1, 2, 3, 4, 5, 6, 7, 8},
    797 		src_port => 1234,
    798 		dest_address => {65535, 55555, 2222, 333, 1, 9999, 777, 8},
    799 		dest_port => 23456
    800 	},
    801 	{ok, Test3, <<>>} = parse(iolist_to_binary(header(Test3))),
    802 	Test4 = #{
    803 		version => 2,
    804 		command => proxy,
    805 		transport_family => unix,
    806 		transport_protocol => dgram,
    807 		src_address => <<"/run/source.sock">>,
    808 		dest_address => <<"/run/destination.sock">>
    809 	},
    810 	{ok, Test4, <<>>} = parse(iolist_to_binary(header(Test4))),
    811 	ok.
    812 
    813 v2_tlvs_test() ->
    814 	Common = #{
    815 		version => 2,
    816 		command => proxy,
    817 		transport_family => ipv4,
    818 		transport_protocol => stream,
    819 		src_address => {127, 0, 0, 1},
    820 		src_port => 1234,
    821 		dest_address => {10, 11, 12, 13},
    822 		dest_port => 23456
    823 	},
    824 	Test1 = Common#{alpn => <<"h2">>},
    825 	{ok, Test1, <<>>} = parse(iolist_to_binary(header(Test1))),
    826 	Test2 = Common#{authority => <<"internal.example.org">>},
    827 	{ok, Test2, <<>>} = parse(iolist_to_binary(header(Test2))),
    828 	Test3 = Common#{netns => <<"/var/run/netns/example">>},
    829 	{ok, Test3, <<>>} = parse(iolist_to_binary(header(Test3))),
    830 	Test4 = Common#{ssl => #{
    831 		client => [ssl, cert_conn, cert_sess],
    832 		verified => true,
    833 		version => <<"TLSv1.3">>, %% Note that I'm not sure this example value is correct.
    834 		cipher => <<"ECDHE-RSA-AES128-GCM-SHA256">>,
    835 		sig_alg => <<"SHA256">>,
    836 		key_alg => <<"RSA2048">>,
    837 		cn => <<"example.com">>
    838 	}},
    839 	{ok, Test4, <<>>} = parse(iolist_to_binary(header(Test4))),
    840 	%% Note that the raw_tlvs order is not relevant and therefore
    841 	%% the parser does not reverse the list it builds.
    842 	Test5In = Common#{raw_tlvs => RawTLVs=[
    843 		%% The only custom TLV I am aware of is defined at:
    844 		%% https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol
    845 		{16#ea, <<16#1, "instance-id">>},
    846 		%% This TLV is entirely fictional.
    847 		{16#ff, <<1, 2, 3, 4, 5, 6, 7, 8, 9, 0>>}
    848 	]},
    849 	Test5Out = Test5In#{raw_tlvs => lists:reverse(RawTLVs)},
    850 	{ok, Test5Out, <<>>} = parse(iolist_to_binary(header(Test5In))),
    851 	ok.
    852 
    853 v2_checksum_test() ->
    854 	Test = #{
    855 		version => 2,
    856 		command => proxy,
    857 		transport_family => ipv4,
    858 		transport_protocol => stream,
    859 		src_address => {127, 0, 0, 1},
    860 		src_port => 1234,
    861 		dest_address => {10, 11, 12, 13},
    862 		dest_port => 23456
    863 	},
    864 	{ok, Test, <<>>} = parse(iolist_to_binary(header(Test, #{checksum => crc32c}))),
    865 	ok.
    866 
    867 v2_padding_test() ->
    868 	Test = #{
    869 		version => 2,
    870 		command => proxy,
    871 		transport_family => ipv4,
    872 		transport_protocol => stream,
    873 		src_address => {127, 0, 0, 1},
    874 		src_port => 1234,
    875 		dest_address => {10, 11, 12, 13},
    876 		dest_port => 23456
    877 	},
    878 	{ok, Test, <<>>} = parse(iolist_to_binary(header(Test, #{padding => 123}))),
    879 	ok.
    880 -endif.