diff options
Diffstat (limited to 'erts/emulator/test/decode_packet_SUITE.erl')
-rw-r--r-- | erts/emulator/test/decode_packet_SUITE.erl | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl new file mode 100644 index 0000000000..13f17e972c --- /dev/null +++ b/erts/emulator/test/decode_packet_SUITE.erl @@ -0,0 +1,514 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% Test suite for erlang:decode_packet/3 + +-module(decode_packet_SUITE). + +-include("test_server.hrl"). + +-export([all/1,init_per_testcase/2,fin_per_testcase/2, + basic/1, packet_size/1, neg/1, http/1, line/1, ssl/1]). + +all(suite) -> + [basic, packet_size, neg, http, line, ssl]. + +init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> + Seed = {S1,S2,S3} = now(), + random:seed(S1,S2,S3), + io:format("*** SEED: ~p ***\n", [Seed]), + Dog=?t:timetrap(?t:minutes(1)), + [{watchdog, Dog}|Config]. + +fin_per_testcase(_Func, Config) -> + Dog=?config(watchdog, Config), + ?t:timetrap_cancel(Dog). + +basic(doc) -> []; +basic(suite) -> []; +basic(Config) when is_list(Config) -> + ?line Packet = <<101,22,203,54,175>>, + ?line Rest = <<123,34,0,250>>, + ?line Bin = <<Packet/binary,Rest/binary>>, + ?line {ok, Bin, <<>>} = decode_pkt(raw,Bin), + + ?line {more, 5+1} = decode_pkt(1,<<5,1,2,3,4>>), + ?line {more, 5+2} = decode_pkt(2,<<0,5,1,2,3,4>>), + ?line {more, 5+4} = decode_pkt(4,<<0,0,0,5,1,2,3,4>>), + + ?line {more, undefined} = decode_pkt(1,<<>>), + ?line {more, undefined} = decode_pkt(2,<<0>>), + ?line {more, undefined} = decode_pkt(4,<<0,0,0>>), + + Types = [1,2,4,asn1,sunrm,cdr,fcgi,tpkt,ssl_tls], + + %% Run tests for different header types and bit offsets. + + lists:foreach(fun({Type,Bits})->basic_pack(Type,Packet,Rest,Bits), + more_length(Type,Packet,Bits) end, + [{T,B} || T<-Types, B<-lists:seq(0,32)]), + ok. + +basic_pack(Type,Body,Rest,BitOffs) -> + ?line {Bin,Unpacked,_} = pack(Type,Body,Rest,BitOffs), + ?line {ok, Unpacked, Rest} = decode_pkt(Type,Bin), + case Rest of + <<>> -> ok; + _ -> + ?line <<_:1,NRest/bits>> = Rest, + basic_pack(Type,Body,NRest,BitOffs) + end. + +more_length(Type,Body,BitOffs) -> + ?line {Bin,_,_} = pack(Type,Body,<<>>,BitOffs), + HdrSize = byte_size(Bin) - byte_size(Body), + more_length_do(Type,HdrSize,Bin,byte_size(Bin)). + +more_length_do(_,_,_,0) -> + ok; +more_length_do(Type,HdrSize,Bin,Size) -> + TrySize = (Size*3) div 4, + NSize = if TrySize < HdrSize -> Size - 1; + true -> TrySize + end, + {B1,_} = split_binary(Bin,NSize), + ?line {more, Length} = decode_pkt(Type,B1), + case Length of + L when L=:=byte_size(Bin) -> ok; + undefined when NSize<HdrSize -> ok + end, + more_length_do(Type,HdrSize,Bin,NSize). + + + +pack(Type,Packet,Rest) -> + {Bin,Unpacked} = pack(Type,Packet), + {<<Bin/binary,Rest/bits>>,Unpacked}. + +%pack(0,B,R,Bits) -> +% pack(raw,B,R,Bits); +%pack(raw,Body,Rest,BitOffs) -> +% Orig = <<0:BitOffs,Body/binary,Rest/bits>>, +% <<_:BitOffs,Bin/bits>> = Orig, +% {Bin,<<Bin/binary,Rest/bits>>,Orig}; +pack(Type,Body,Rest,BitOffs) -> + {Packet,Unpacked} = pack(Type,Body), + + %% Make Bin a sub-bin with an arbitrary bitoffset within Orig + Prefix = random:uniform(1 bsl BitOffs) - 1, + Orig = <<Prefix:BitOffs,Packet/binary,Rest/bits>>, + <<_:BitOffs,Bin/bits>> = Orig, + {Bin,Unpacked,Orig}. + +pack(1,Bin) -> + Psz = byte_size(Bin), + {<<Psz:8,Bin/binary>>, Bin}; +pack(2,Bin) -> + Psz = byte_size(Bin), + {<<Psz:16,Bin/binary>>, Bin}; +pack(4,Bin) -> + Psz = byte_size(Bin), + {<<Psz:32,Bin/binary>>, Bin}; +pack(asn1,Bin) -> + Ident = case random:uniform(3) of + 1 -> <<17>>; + 2 -> <<16#1f,16#81,17>>; + 3 -> <<16#1f,16#81,16#80,16#80,17>> + end, + Psz = byte_size(Bin), + Length = case random:uniform(4) of + 1 when Psz < 128 -> + <<Psz:8>>; + R when R=<2 andalso Psz < 16#10000 -> + <<16#82,Psz:16>>; + R when R=<3 andalso Psz < 16#1000000 -> + <<16#83,Psz:24>>; + _ when Psz < 16#100000000 -> + <<16#84,Psz:32>> + end, + Res = <<Ident/binary,Length/binary,Bin/binary>>, + {Res,Res}; +pack(sunrm,Bin) -> + Psz = byte_size(Bin), + Res = if Psz < 16#80000000 -> + <<Psz:32,Bin/binary>> + end, + {Res,Res}; +pack(cdr,Bin) -> + GIOP = <<"GIOP">>, + Major = random:uniform(256) - 1, + Minor = random:uniform(256) - 1, + MType = random:uniform(256) - 1, + Psz = byte_size(Bin), + Res = case random:uniform(2) of + 1 -> <<GIOP/binary,Major:8,Minor:8,0:8,MType:8,Psz:32/big,Bin/binary>>; + 2 -> <<GIOP/binary,Major:8,Minor:8,1:8,MType:8,Psz:32/little,Bin/binary>> + end, + {Res,Res}; +pack(fcgi,Bin) -> + Ver = 1, + Type = random:uniform(256) - 1, + Id = random:uniform(65536) - 1, + PaddSz = random:uniform(16) - 1, + Psz = byte_size(Bin), + Reserv = random:uniform(256) - 1, + Padd = case PaddSz of + 0 -> <<>>; + _ -> list_to_binary([random:uniform(256)-1 + || _<- lists:seq(1,PaddSz)]) + end, + Res = <<Ver:8,Type:8,Id:16,Psz:16/big,PaddSz:8,Reserv:8,Bin/binary>>, + {<<Res/binary,Padd/binary>>, Res}; +pack(tpkt,Bin) -> + Ver = 3, + Reserv = random:uniform(256) - 1, + Size = byte_size(Bin) + 4, + Res = <<Ver:8,Reserv:8,Size:16,Bin/binary>>, + {Res, Res}; +pack(ssl_tls,Bin) -> + Content = case (random:uniform(256) - 1) of + C when C<128 -> C; + _ -> v2hello + end, + Major = random:uniform(256) - 1, + Minor = random:uniform(256) - 1, + pack_ssl(Content,Major,Minor,Bin). + +pack_ssl(Content, Major, Minor, Body) -> + case Content of + v2hello -> + Size = byte_size(Body), + Res = <<1:1,(Size+3):15, 1:8, Major:8, Minor:8, Body/binary>>, + C = 22, + Data = <<1:8, (Size+2):24, Major:8, Minor:8, Body/binary>>; + C when is_integer(C) -> + Size = byte_size(Body), + Res = <<Content:8, Major:8, Minor:8, Size:16, Body/binary>>, + Data = Body + end, + {Res, {ssl_tls,[],C,{Major,Minor}, Data}}. + + +packet_size(doc) -> []; +packet_size(suite) -> []; +packet_size(Config) when is_list(Config) -> + ?line Packet = <<101,22,203,54,175>>, + ?line Rest = <<123,34,0,250>>, + + F = fun({Type,Max})-> + ?line {Bin,Unpacked} = pack(Type,Packet,Rest), + ?line case decode_pkt(Type,Bin,[{packet_size,Max}]) of + {ok,Unpacked,Rest} when Max=:=0; Max>=byte_size(Packet) -> + ok; + {error,_} when Max<byte_size(Packet), Max=/=0 -> + ok; + {error,_} when Type=:=fcgi, Max=/=0 -> + %% packet includes random amount of padding + ok + end + end, + ?line lists:foreach(F, [{T,D} || T<-[1,2,4,asn1,sunrm,cdr,fcgi,tpkt,ssl_tls], + D<-lists:seq(0, byte_size(Packet)*2)]), + + %% Test OTP-8102, "negative" 4-byte sizes. + lists:foreach(fun(Size) -> + ?line {error,_} = decode_pkt(4,<<Size:32,Packet/binary>>) + end, + lists:seq(-10,-1)), + ok. + + +neg(doc) -> []; +neg(suite) -> []; +neg(Config) when is_list(Config) -> + ?line Bin = <<"dummy">>, + Fun = fun()->dummy end, + + BadargF = fun(T,B,Opts)-> {'EXIT',{badarg,_}} = (catch decode_pkt(T,B,Opts)) end, + + %% Invalid Type args + lists:foreach(fun(T)-> BadargF(T,Bin,[]) end, + [3,-1,5,2.0,{2},unknown,[],"line",Bin,Fun,self()]), + + %% Invalid Bin args + lists:foreach(fun(B)-> BadargF(0,B,[]) end, + [3,2.0,unknown,[],"Bin",[Bin],{Bin},Fun,self()]), + + %% Invalid options + InvOpts = [2,false,self(),Bin,"Options",Fun, + packet_size,{packet_size},{packet_size,0,false}, + {packet_size,-1},{packet_size,100.0},{packet_size,false}, + {line_length,-1},{line_length,100.0},{line_length,false}], + + lists:foreach(fun(Opt)-> BadargF(0,Bin,Opt), + BadargF(0,Bin,[Opt]), + BadargF(0,Bin,[Opt,{packet_size,1000}]), + BadargF(0,Bin,[{packet_size,1000},Opt]) end, + InvOpts), + ok. + + +http(doc) -> []; +http(suite) -> []; +http(Config) when is_list(Config) -> + ?line <<"foo">> = http_do(http_request("foo")), + ?line <<" bar">> = http_do(http_request(" bar")), + ?line <<"Hello!">> = http_do(http_response("Hello!")), + + %% Test all known header atoms + Val = "dummy value", + ValB = list_to_binary(Val), + Rest = <<"Rest">>, + HdrF = fun(Str,N) -> + ?line StrA = list_to_atom(Str), + ?line StrB = list_to_binary(Str), + ?line Bin = <<StrB/binary,": ",ValB/binary,"\r\n",Rest/binary>>, + ?line {ok, {http_header,N,StrA,undefined,Val}, Rest} = decode_pkt(httph,Bin), + ?line {ok, {http_header,N,StrA,undefined,ValB}, Rest} = decode_pkt(httph_bin,Bin), + ?line N + 1 + end, + ?line lists:foldl(HdrF, 1, http_hdr_strings()), + + %% Test all known method atoms + MethF = fun(Meth) -> + ?line MethA = list_to_atom(Meth), + ?line MethB = list_to_binary(Meth), + ?line Bin = <<MethB/binary," /invalid/url HTTP/1.0\r\n",Rest/binary>>, + ?line {ok, {http_request,MethA,{abs_path,"/invalid/url"},{1,0}}, + Rest} = decode_pkt(http,Bin), + ?line {ok, {http_request,MethA,{abs_path,<<"/invalid/url">>},{1,0}}, + Rest} = decode_pkt(http_bin,Bin) + end, + ?line lists:foreach(MethF, http_meth_strings()), + + %% Test all uri variants + UriF = fun({Str,ResL,ResB}) -> + Bin = <<"GET ",(list_to_binary(Str))/binary," HTTP/1.1\r\n",Rest/binary>>, + {ok, {http_request, 'GET', ResL, {1,1}}, Rest} = decode_pkt(http,Bin), + {ok, {http_request, 'GET', ResB, {1,1}}, Rest} = decode_pkt(http_bin,Bin) + end, + lists:foreach(UriF, http_uri_variants()), + ok. + +http_with_bin(http) -> + http_bin; +http_with_bin(httph) -> + httph_bin. + +http_do(Tup) -> + http_do(Tup,http). +http_do({Bin, []}, _) -> + Bin; +http_do({Bin,[{_Line,PL,PB}|Tail]}, Type) -> + ?line {ok, PL, Rest} = decode_pkt(Type,Bin), + ?line {ok, PB, Rest} = decode_pkt(http_with_bin(Type),Bin), + + %% Same tests again but as SubBin + PreLen = random:uniform(64), + Prefix = random:uniform(1 bsl PreLen) - 1, + SufLen = random:uniform(64), + Suffix = random:uniform(1 bsl SufLen) - 1, + Orig = <<Prefix:PreLen, Bin/bits, Suffix:SufLen>>, + BinLen = bit_size(Bin), + <<_:PreLen, SubBin:BinLen/bits, _/bits>> = Orig, % Make SubBin + ?line SubBin = Bin, % just to make sure + + ?line {ok, PL, Rest} = decode_pkt(Type,SubBin), + ?line {ok, PB, Rest} = decode_pkt(http_with_bin(Type),SubBin), + http_do({Rest, Tail}, httph). + +http_request(Msg) -> + QnA = [{"POST /invalid/url HTTP/1.1\r\n", + {http_request, 'POST', {abs_path, "/invalid/url" }, {1,1}}, + {http_request, 'POST', {abs_path,<<"/invalid/url">>}, {1,1}}}, + {"Connection: close\r\n", + {http_header,2,'Connection',undefined, "close"}, + {http_header,2,'Connection',undefined,<<"close">>}}, + {"Host\t : localhost:8000\r\n", % white space before : + {http_header,14,'Host',undefined, "localhost:8000"}, + {http_header,14,'Host',undefined,<<"localhost:8000">>}}, + {"User-Agent: perl post\r\n", + {http_header,24,'User-Agent',undefined, "perl post"}, + {http_header,24,'User-Agent',undefined,<<"perl post">>}}, + {"Content-Length: 4\r\n", + {http_header,38,'Content-Length',undefined, "4"}, + {http_header,38,'Content-Length',undefined,<<"4">>}}, + {"Content-Type: text/xml; charset=utf-8\r\n", + {http_header,42,'Content-Type',undefined, "text/xml; charset=utf-8"}, + {http_header,42,'Content-Type',undefined,<<"text/xml; charset=utf-8">>}}, + {"Other-Field: with some text\r\n", + {http_header,0, "Other-Field" ,undefined, "with some text"}, + {http_header,0,<<"Other-Field">>,undefined,<<"with some text">>}}, + {"Multi-Line: Once upon a time in a land far far away,\r\n" + " there lived a princess imprisoned in the highest tower\r\n" + " of the most haunted castle.\r\n", + {http_header,0, "Multi-Line" ,undefined, "Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle."}, + {http_header,0,<<"Multi-Line">>,undefined,<<"Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle.">>}}, + {"\r\n", + http_eoh, + http_eoh}], + Bin = lists:foldl(fun({Line,_,_},Acc) -> LineBin = list_to_binary(Line), + <<Acc/binary,LineBin/binary>> end, + <<"">>, QnA), + MsgBin = list_to_binary(Msg), + {<<Bin/binary,MsgBin/binary>>, QnA}. + + +http_response(Msg) -> + QnA = [{"HTTP/1.0 404 Object Not Found\r\n", + {http_response, {1,0}, 404, "Object Not Found"}, + {http_response, {1,0}, 404, <<"Object Not Found">>}}, + {"Server: inets/4.7.16\r\n", + {http_header, 30, 'Server', undefined, "inets/4.7.16"}, + {http_header, 30, 'Server', undefined, <<"inets/4.7.16">>}}, + {"Date: Fri, 04 Jul 2008 17:16:22 GMT\r\n", + {http_header, 3, 'Date', undefined, "Fri, 04 Jul 2008 17:16:22 GMT"}, + {http_header, 3, 'Date', undefined, <<"Fri, 04 Jul 2008 17:16:22 GMT">>}}, + {"Content-Type: text/html\r\n", + {http_header, 42, 'Content-Type', undefined, "text/html"}, + {http_header, 42, 'Content-Type', undefined, <<"text/html">>}}, + {"Content-Length: 207\r\n", + {http_header, 38, 'Content-Length', undefined, "207"}, + {http_header, 38, 'Content-Length', undefined, <<"207">>}}, + {"\r\n", + http_eoh, + http_eoh}], + + + + Bin = lists:foldl(fun({Line,_,_},Acc) -> LineBin = list_to_binary(Line), + <<Acc/binary,LineBin/binary>> end, + <<"">>, QnA), + MsgBin = list_to_binary(Msg), + {<<Bin/binary,MsgBin/binary>>, QnA}. + +http_hdr_strings() -> + %% Must be correct order + ["Cache-Control","Connection","Date","Pragma","Transfer-Encoding", + "Upgrade","Via","Accept", "Accept-Charset", "Accept-Encoding", + "Accept-Language", "Authorization","From","Host","If-Modified-Since", + "If-Match","If-None-Match","If-Range","If-Unmodified-Since","Max-Forwards", + "Proxy-Authorization","Range","Referer","User-Agent","Age","Location", + "Proxy-Authenticate","Public","Retry-After","Server","Vary","Warning", + "Www-Authenticate","Allow","Content-Base","Content-Encoding", + "Content-Language","Content-Length","Content-Location","Content-Md5", + "Content-Range","Content-Type","Etag","Expires","Last-Modified", + "Accept-Ranges","Set-Cookie","Set-Cookie2","X-Forwarded-For","Cookie", + "Keep-Alive","Proxy-Connection"]. + +http_meth_strings() -> + ["OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE"]. + +http_uri_variants() -> + [{"*", '*', '*'}, + {"http://tools.ietf.org/html/rfc3986", + {absoluteURI,http, "tools.ietf.org", undefined, "/html/rfc3986"}, + {absoluteURI,http,<<"tools.ietf.org">>,undefined,<<"/html/rfc3986">>}}, + {"http://otp.ericsson.se:8000/product/internal/", + {absoluteURI,http, "otp.ericsson.se" ,8000, "/product/internal/"}, + {absoluteURI,http,<<"otp.ericsson.se">>,8000,<<"/product/internal/">>}}, + {"https://example.com:8042/over/there?name=ferret#nose", + {absoluteURI,https, "example.com", 8042, "/over/there?name=ferret#nose"}, + {absoluteURI,https,<<"example.com">>,8042,<<"/over/there?name=ferret#nose">>}}, + {"ftp://cnn.example.com&[email protected]/top_story.htm", + {scheme, "ftp", "//cnn.example.com&[email protected]/top_story.htm"}, + {scheme,<<"ftp">>,<<"//cnn.example.com&[email protected]/top_story.htm">>}}, + {"/some/absolute/path", + {abs_path, "/some/absolute/path"}, + {abs_path,<<"/some/absolute/path">>}}, + {"something_else", "something_else", <<"something_else">>}]. + + +line(doc) -> []; +line(suite) -> []; +line(Config) when is_list(Config) -> + Text = <<"POST /invalid/url HTTP/1.1\r\n" + "Connection: close\r\n" + "Host\t : localhost:8000\r\n" + "User-Agent: perl post\r\n" + "Content-Length: 4\r\n" + "Content-Type: text/xml; charset=utf-8\r\n" + "Other-Field: with some text\r\n" + "Multi-Line: Once upon a time in a land far far away,\r\n" + " there lived a princess imprisoned in the highest tower\r\n" + " of the most haunted castle.\r\n" + "\r\nThe residue">>, + + lists:foreach(fun(MaxLen) -> line_do(Text,MaxLen) end, + [0,7,19,29,37]), + ok. + +line_do(Bin,MaxLen) -> + Res = decode_pkt(line,Bin,[{line_length,MaxLen}]), + MyRes = decode_line(Bin,MaxLen), + ?line MyRes = Res, + case Res of + {ok,_,Rest} -> + line_do(Rest,MaxLen); + {more,undefined} -> + ok + end. + +% Emulates decode_packet(line,Bin,[{line_length,MaxLen}]) +decode_line(Bin,MaxLen) -> + ?line case find_in_binary($\n,Bin) of + notfound when MaxLen>0 andalso byte_size(Bin) >= MaxLen -> + {LineB,Rest} = split_binary(Bin,MaxLen), + {ok,LineB,Rest}; + notfound -> + {more,undefined}; + Pos when MaxLen>0 andalso Pos > MaxLen -> + {LineB,Rest} = split_binary(Bin,MaxLen), + {ok,LineB,Rest}; + Pos -> + {LineB,Rest} = split_binary(Bin,Pos), + {ok,LineB,Rest} + end. + +find_in_binary(Byte, Bin) -> + case string:chr(binary_to_list(Bin),Byte) of + 0 -> notfound; + P -> P + end. + +ssl(doc) -> []; +ssl(suite) -> []; +ssl(Config) when is_list(Config) -> + Major = 34, + Minor = 17, + Body = <<234,189,73,199,1,32,4,0,254>>, + Rest = <<23,123,203,12,234>>, + + F = fun(Content) -> + {Packet,Unpacked} = pack_ssl(Content, Major, Minor, Body), + Bin = <<Packet/binary,Rest/binary>>, + ?line {ok, Unpacked, Rest} = decode_pkt(ssl_tls, Bin) + end, + F(25), + F(v2hello), + ok. + +decode_pkt(Type,Bin) -> + decode_pkt(Type,Bin,[]). +decode_pkt(Type,Bin,Opts) -> + %%io:format("decode_packet(~p,~p,~p)\n",[Type,Bin,Opts]), + Res = erlang:decode_packet(Type,Bin,Opts), + %%io:format(" -> ~p\n",[Res]), + Res. + |