aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2018-11-26 11:52:27 +0100
committerLoïc Hoguin <[email protected]>2018-11-26 11:52:27 +0100
commit0d9ad0a8445e7ba6e8bf9591257fa4457dda8321 (patch)
tree456dc3ad8457c00c9870bc32f40195d4430104e0
parent7423a7bb6dde5edc26283544956ae7387fa69688 (diff)
downloadranch-0d9ad0a8445e7ba6e8bf9591257fa4457dda8321.tar.gz
ranch-0d9ad0a8445e7ba6e8bf9591257fa4457dda8321.tar.bz2
ranch-0d9ad0a8445e7ba6e8bf9591257fa4457dda8321.zip
Use the correct CRC32 algorithm for the PROXY header
Turns out it wasn't the plain CRC32 algorithm that should have been used, whoops! The implementation was adapted from pseudo code from Thomas Cioppettini. I've renamed the PROXY header building option value for checksum from crc32 to crc32c and updated the documentation. There is no support for plain crc32 checksums.
-rw-r--r--doc/src/manual/ranch_proxy_header.header.asciidoc4
-rw-r--r--ebin/ranch.app2
-rw-r--r--src/ranch_crc32c.erl115
-rw-r--r--src/ranch_proxy_header.erl26
4 files changed, 137 insertions, 10 deletions
diff --git a/doc/src/manual/ranch_proxy_header.header.asciidoc b/doc/src/manual/ranch_proxy_header.header.asciidoc
index 4dc5044..c551110 100644
--- a/doc/src/manual/ranch_proxy_header.header.asciidoc
+++ b/doc/src/manual/ranch_proxy_header.header.asciidoc
@@ -13,7 +13,7 @@ header(ProxyInfo, BuildOpts) -> iodata()
ProxyInfo :: ranch_proxy_header:proxy_info()
BuildOpts :: #{
- checksum => crc32,
+ checksum => crc32c,
padding => pos_integer() %% >= 3
}
----
@@ -63,7 +63,7 @@ Data = ranch_proxy_header:parse(ProxyInfo).
[source,erlang]
----
Data = ranch_proxy_header:parse(ProxyInfo, #{
- checksum => crc32,
+ checksum => crc32c,
padding => 7
}).
----
diff --git a/ebin/ranch.app b/ebin/ranch.app
index 82a4bbc..e001a94 100644
--- a/ebin/ranch.app
+++ b/ebin/ranch.app
@@ -1,7 +1,7 @@
{application, 'ranch', [
{description, "Socket acceptor pool for TCP protocols."},
{vsn, "1.7.0"},
- {modules, ['ranch','ranch_acceptor','ranch_acceptors_sup','ranch_app','ranch_conns_sup','ranch_listener_sup','ranch_protocol','ranch_proxy_header','ranch_server','ranch_ssl','ranch_sup','ranch_tcp','ranch_transport']},
+ {modules, ['ranch','ranch_acceptor','ranch_acceptors_sup','ranch_app','ranch_conns_sup','ranch_crc32c','ranch_listener_sup','ranch_protocol','ranch_proxy_header','ranch_server','ranch_ssl','ranch_sup','ranch_tcp','ranch_transport']},
{registered, [ranch_sup,ranch_server]},
{applications, [kernel,stdlib,ssl]},
{mod, {ranch_app, []}},
diff --git a/src/ranch_crc32c.erl b/src/ranch_crc32c.erl
new file mode 100644
index 0000000..fc9be35
--- /dev/null
+++ b/src/ranch_crc32c.erl
@@ -0,0 +1,115 @@
+%% Copyright (c) 2018, Loïc Hoguin <[email protected]>
+%%
+%% Permission to use, copy, modify, and/or distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-module(ranch_crc32c).
+
+-export([crc32c/1]).
+-export([crc32c/2]).
+
+-define(CRC32C_TABLE, {
+ 16#00000000, 16#F26B8303, 16#E13B70F7, 16#1350F3F4,
+ 16#C79A971F, 16#35F1141C, 16#26A1E7E8, 16#D4CA64EB,
+ 16#8AD958CF, 16#78B2DBCC, 16#6BE22838, 16#9989AB3B,
+ 16#4D43CFD0, 16#BF284CD3, 16#AC78BF27, 16#5E133C24,
+ 16#105EC76F, 16#E235446C, 16#F165B798, 16#030E349B,
+ 16#D7C45070, 16#25AFD373, 16#36FF2087, 16#C494A384,
+ 16#9A879FA0, 16#68EC1CA3, 16#7BBCEF57, 16#89D76C54,
+ 16#5D1D08BF, 16#AF768BBC, 16#BC267848, 16#4E4DFB4B,
+ 16#20BD8EDE, 16#D2D60DDD, 16#C186FE29, 16#33ED7D2A,
+ 16#E72719C1, 16#154C9AC2, 16#061C6936, 16#F477EA35,
+ 16#AA64D611, 16#580F5512, 16#4B5FA6E6, 16#B93425E5,
+ 16#6DFE410E, 16#9F95C20D, 16#8CC531F9, 16#7EAEB2FA,
+ 16#30E349B1, 16#C288CAB2, 16#D1D83946, 16#23B3BA45,
+ 16#F779DEAE, 16#05125DAD, 16#1642AE59, 16#E4292D5A,
+ 16#BA3A117E, 16#4851927D, 16#5B016189, 16#A96AE28A,
+ 16#7DA08661, 16#8FCB0562, 16#9C9BF696, 16#6EF07595,
+ 16#417B1DBC, 16#B3109EBF, 16#A0406D4B, 16#522BEE48,
+ 16#86E18AA3, 16#748A09A0, 16#67DAFA54, 16#95B17957,
+ 16#CBA24573, 16#39C9C670, 16#2A993584, 16#D8F2B687,
+ 16#0C38D26C, 16#FE53516F, 16#ED03A29B, 16#1F682198,
+ 16#5125DAD3, 16#A34E59D0, 16#B01EAA24, 16#42752927,
+ 16#96BF4DCC, 16#64D4CECF, 16#77843D3B, 16#85EFBE38,
+ 16#DBFC821C, 16#2997011F, 16#3AC7F2EB, 16#C8AC71E8,
+ 16#1C661503, 16#EE0D9600, 16#FD5D65F4, 16#0F36E6F7,
+ 16#61C69362, 16#93AD1061, 16#80FDE395, 16#72966096,
+ 16#A65C047D, 16#5437877E, 16#4767748A, 16#B50CF789,
+ 16#EB1FCBAD, 16#197448AE, 16#0A24BB5A, 16#F84F3859,
+ 16#2C855CB2, 16#DEEEDFB1, 16#CDBE2C45, 16#3FD5AF46,
+ 16#7198540D, 16#83F3D70E, 16#90A324FA, 16#62C8A7F9,
+ 16#B602C312, 16#44694011, 16#5739B3E5, 16#A55230E6,
+ 16#FB410CC2, 16#092A8FC1, 16#1A7A7C35, 16#E811FF36,
+ 16#3CDB9BDD, 16#CEB018DE, 16#DDE0EB2A, 16#2F8B6829,
+ 16#82F63B78, 16#709DB87B, 16#63CD4B8F, 16#91A6C88C,
+ 16#456CAC67, 16#B7072F64, 16#A457DC90, 16#563C5F93,
+ 16#082F63B7, 16#FA44E0B4, 16#E9141340, 16#1B7F9043,
+ 16#CFB5F4A8, 16#3DDE77AB, 16#2E8E845F, 16#DCE5075C,
+ 16#92A8FC17, 16#60C37F14, 16#73938CE0, 16#81F80FE3,
+ 16#55326B08, 16#A759E80B, 16#B4091BFF, 16#466298FC,
+ 16#1871A4D8, 16#EA1A27DB, 16#F94AD42F, 16#0B21572C,
+ 16#DFEB33C7, 16#2D80B0C4, 16#3ED04330, 16#CCBBC033,
+ 16#A24BB5A6, 16#502036A5, 16#4370C551, 16#B11B4652,
+ 16#65D122B9, 16#97BAA1BA, 16#84EA524E, 16#7681D14D,
+ 16#2892ED69, 16#DAF96E6A, 16#C9A99D9E, 16#3BC21E9D,
+ 16#EF087A76, 16#1D63F975, 16#0E330A81, 16#FC588982,
+ 16#B21572C9, 16#407EF1CA, 16#532E023E, 16#A145813D,
+ 16#758FE5D6, 16#87E466D5, 16#94B49521, 16#66DF1622,
+ 16#38CC2A06, 16#CAA7A905, 16#D9F75AF1, 16#2B9CD9F2,
+ 16#FF56BD19, 16#0D3D3E1A, 16#1E6DCDEE, 16#EC064EED,
+ 16#C38D26C4, 16#31E6A5C7, 16#22B65633, 16#D0DDD530,
+ 16#0417B1DB, 16#F67C32D8, 16#E52CC12C, 16#1747422F,
+ 16#49547E0B, 16#BB3FFD08, 16#A86F0EFC, 16#5A048DFF,
+ 16#8ECEE914, 16#7CA56A17, 16#6FF599E3, 16#9D9E1AE0,
+ 16#D3D3E1AB, 16#21B862A8, 16#32E8915C, 16#C083125F,
+ 16#144976B4, 16#E622F5B7, 16#F5720643, 16#07198540,
+ 16#590AB964, 16#AB613A67, 16#B831C993, 16#4A5A4A90,
+ 16#9E902E7B, 16#6CFBAD78, 16#7FAB5E8C, 16#8DC0DD8F,
+ 16#E330A81A, 16#115B2B19, 16#020BD8ED, 16#F0605BEE,
+ 16#24AA3F05, 16#D6C1BC06, 16#C5914FF2, 16#37FACCF1,
+ 16#69E9F0D5, 16#9B8273D6, 16#88D28022, 16#7AB90321,
+ 16#AE7367CA, 16#5C18E4C9, 16#4F48173D, 16#BD23943E,
+ 16#F36E6F75, 16#0105EC76, 16#12551F82, 16#E03E9C81,
+ 16#34F4F86A, 16#C69F7B69, 16#D5CF889D, 16#27A40B9E,
+ 16#79B737BA, 16#8BDCB4B9, 16#988C474D, 16#6AE7C44E,
+ 16#BE2DA0A5, 16#4C4623A6, 16#5F16D052, 16#AD7D5351
+}).
+
+%% The interface mirrors erlang:crc32/1,2.
+-spec crc32c(iodata()) -> non_neg_integer().
+crc32c(Data) ->
+ do_crc32c(16#ffffffff, iolist_to_binary(Data)).
+
+-spec crc32c(CRC, iodata()) -> CRC when CRC::non_neg_integer().
+crc32c(OldCrc, Data) ->
+ do_crc32c(OldCrc bxor 16#ffffffff, iolist_to_binary(Data)).
+
+do_crc32c(OldCrc, <<C, Rest/bits>>) ->
+ do_crc32c((OldCrc bsr 8) bxor element(1 + ((OldCrc bxor C) band 16#ff), ?CRC32C_TABLE),
+ Rest);
+do_crc32c(OldCrc, <<>>) ->
+ OldCrc bxor 16#ffffffff.
+
+-ifdef(TEST).
+crc32c_test_() ->
+ Tests = [
+ %% Tests from RFC3720 B.4.
+ {<<0:32/unit:8>>, 16#8a9136aa},
+ {iolist_to_binary([16#ff || _ <- lists:seq(1, 32)]), 16#62a8ab43},
+ {iolist_to_binary([N || N <- lists:seq(0, 16#1f)]), 16#46dd794e},
+ {iolist_to_binary([N || N <- lists:seq(16#1f, 0, -1)]), 16#113fdb5c},
+ {<<16#01c00000:32, 0:32, 0:32, 0:32, 16#14000000:32, 16#00000400:32, 16#00000014:32,
+ 16#00000018:32, 16#28000000:32, 0:32, 16#02000000:32, 0:32>>, 16#d9963a56}
+ ],
+ [{iolist_to_binary(io_lib:format("16#~8.16.0b", [R])),
+ fun() -> R = crc32c(V) end} || {V, R} <- Tests].
+-endif.
diff --git a/src/ranch_proxy_header.erl b/src/ranch_proxy_header.erl
index 837ce2d..081157f 100644
--- a/src/ranch_proxy_header.erl
+++ b/src/ranch_proxy_header.erl
@@ -48,7 +48,7 @@
-export_type([proxy_info/0]).
-type build_opts() :: #{
- checksum => crc32,
+ checksum => crc32c,
padding => pos_integer() %% >= 3
}.
@@ -482,6 +482,18 @@ parse_v2_test() ->
Path/binary, 0:Padding,
"GET / HTTP/1.1\r\n">>),
ok.
+
+parse_v2_regression_test() ->
+ %% Real packet received from AWS. We confirm that the CRC32C
+ %% check succeeds only (in other words that ok is returned).
+ {ok, _, <<>>} = parse(<<
+ 13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, 33, 17, 0, 84,
+ 172, 31, 7, 113, 172, 31, 10, 31, 200, 242, 0, 80, 3, 0, 4,
+ 232, 214, 137, 45, 234, 0, 23, 1, 118, 112, 99, 101, 45, 48,
+ 56, 100, 50, 98, 102, 49, 53, 102, 97, 99, 53, 48, 48, 49, 99,
+ 57, 4, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>),
+ ok.
-endif.
parse_tlv(Rest, 0, Info, _) ->
@@ -497,8 +509,8 @@ parse_tlv(<<16#3, TLVLen:16, CRC32C:32, Rest/bits>>, Len0, Info, Header) when TL
Len = Len0 - TLVLen - 3,
BeforeLen = byte_size(Header) - Len - TLVLen,
<<Before:BeforeLen/binary, _:32, After:Len/binary>> = Header,
- %% The initial CRC is erlang:crc32(<<"\r\n\r\n\0\r\nQUIT\n", 2:4, 1:4>>).
- case erlang:crc32(1302506282, [Before, <<0:32>>, After]) of
+ %% The initial CRC is ranch_crc32c:crc32c(<<"\r\n\r\n\0\r\nQUIT\n", 2:4, 1:4>>).
+ case ranch_crc32c:crc32c(2900412422, [Before, <<0:32>>, After]) of
CRC32C ->
parse_tlv(Rest, Len, Info, Header);
_ ->
@@ -591,7 +603,7 @@ header(ProxyInfo=#{version := 2, command := proxy,
Addresses = addresses(ProxyInfo),
TLVs = tlvs(ProxyInfo, Opts),
ExtraLen = case Opts of
- #{checksum := crc32} -> 7;
+ #{checksum := crc32c} -> 7;
_ -> 0
end,
Len = iolist_size(Addresses) + iolist_size(TLVs) + ExtraLen,
@@ -603,8 +615,8 @@ header(ProxyInfo=#{version := 2, command := proxy,
TLVs
],
case Opts of
- #{checksum := crc32} ->
- CRC32C = erlang:crc32([Header, <<16#3, 4:16, 0:32>>]),
+ #{checksum := crc32c} ->
+ CRC32C = ranch_crc32c:crc32c([Header, <<16#3, 4:16, 0:32>>]),
[Header, <<16#3, 4:16, CRC32C:32>>];
_ ->
Header
@@ -849,7 +861,7 @@ v2_checksum_test() ->
dest_address => {10, 11, 12, 13},
dest_port => 23456
},
- {ok, Test, <<>>} = parse(iolist_to_binary(header(Test, #{checksum => crc32}))),
+ {ok, Test, <<>>} = parse(iolist_to_binary(header(Test, #{checksum => crc32c}))),
ok.
v2_padding_test() ->