ranch_proxy_header.erl 27 KB

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