From 96cd83b6efed8ae8a1a0008e24885bae66c1834b Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 8 Feb 2019 11:41:27 +0100 Subject: Use iovec() internally in send path --- lib/ssl/src/ssl.erl | 4 +-- lib/ssl/src/ssl_connection.erl | 6 ++-- lib/ssl/src/tls_record.erl | 78 +++++++++++++++++++++++++----------------- lib/ssl/src/tls_sender.erl | 34 ++++++++++-------- 4 files changed, 70 insertions(+), 52 deletions(-) diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index a7d6f28c7a..4a6e022661 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2018. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -626,7 +626,7 @@ close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _} send(#sslsocket{pid = [Pid]}, Data) when is_pid(Pid) -> ssl_connection:send(Pid, Data); send(#sslsocket{pid = [_, Pid]}, Data) when is_pid(Pid) -> - tls_sender:send_data(Pid, erlang:iolist_to_binary(Data)); + tls_sender:send_data(Pid, erlang:iolist_to_iovec(Data)); send(#sslsocket{pid = {_, #config{transport_info={_, udp, _, _}}}}, _) -> {error,enotconn}; %% Emulate connection behaviour send(#sslsocket{pid = {dtls,_}}, _) -> diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 461f51dd3a..61524347eb 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -211,9 +211,9 @@ socket_control(dtls_connection = Connection, {_, Socket}, [Pid|_] = Pids, Transp %%-------------------------------------------------------------------- send(Pid, Data) -> call(Pid, {application_data, - %% iolist_to_binary should really - %% be called iodata_to_binary() - erlang:iolist_to_binary(Data)}). + %% iolist_to_iovec should really + %% be called iodata_to_iovec() + erlang:iolist_to_iovec(Data)}). %%-------------------------------------------------------------------- -spec recv(pid(), integer(), timeout()) -> diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index e68f9a19df..7d1f2f5e92 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -105,8 +105,8 @@ encode_handshake(Frag, Version, ConnectionStates) -> case iolist_size(Frag) of N when N > ?MAX_PLAIN_TEXT_LENGTH -> - Data = split_bin(iolist_to_binary(Frag), Version, BCA, BeastMitigation), - encode_iolist(?HANDSHAKE, Version, Data, ConnectionStates); + Data = split_iovec(erlang:iolist_to_iovec(Frag), Version, BCA, BeastMitigation), + encode_fragments(?HANDSHAKE, Version, Data, ConnectionStates); _ -> encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates) end. @@ -137,13 +137,13 @@ encode_change_cipher_spec(Version, ConnectionStates) -> %% %% Description: Encodes data to send on the ssl-socket. %%-------------------------------------------------------------------- -encode_data(Frag, Version, +encode_data(Data, Version, #{current_write := #{beast_mitigation := BeastMitigation, security_parameters := #security_parameters{bulk_cipher_algorithm = BCA}}} = ConnectionStates) -> - Data = split_bin(Frag, Version, BCA, BeastMitigation), - encode_iolist(?APPLICATION_DATA, Version, Data, ConnectionStates). + Fragments = split_iovec(Data, Version, BCA, BeastMitigation), + encode_fragments(?APPLICATION_DATA, Version, Fragments, ConnectionStates). %%==================================================================== %% Decoding @@ -520,22 +520,22 @@ binary_from_front(BinSize, [Bin|Front], Size, Rear, Acc) -> %%-------------------------------------------------------------------- encode_plain_text(Type, Version, Data, ConnectionStates0) -> - {[CipherText],ConnectionStates} = encode_iolist(Type, Version, [Data], ConnectionStates0), + {[CipherText],ConnectionStates} = encode_fragments(Type, Version, [Data], ConnectionStates0), {CipherText,ConnectionStates}. %%-------------------------------------------------------------------- -encode_iolist(Type, Version, Data, +encode_fragments(Type, Version, Data, #{current_write := #{compression_state := CompS, cipher_state := CipherS, sequence_number := Seq}} = ConnectionStates) -> - encode_iolist(Type, Version, Data, ConnectionStates, CompS, CipherS, Seq, []). + encode_fragments(Type, Version, Data, ConnectionStates, CompS, CipherS, Seq, []). %% -encode_iolist(_Type, _Version, [], #{current_write := WriteS} = CS, +encode_fragments(_Type, _Version, [], #{current_write := WriteS} = CS, CompS, CipherS, Seq, CipherFragments) -> {lists:reverse(CipherFragments), CS#{current_write := WriteS#{compression_state := CompS, cipher_state := CipherS, sequence_number := Seq}}}; -encode_iolist(Type, Version, [Text|Data], +encode_fragments(Type, Version, [Text|Data], #{current_write := #{security_parameters := #security_parameters{cipher_type = ?AEAD, bulk_cipher_algorithm = BCAlg, @@ -550,9 +550,9 @@ encode_iolist(Type, Version, [Text|Data], {CipherFragment,CipherS} = ssl_record:cipher_aead(Version, CompText, CipherS1, StartAdditionalData, SecPars), Length = byte_size(CipherFragment), CipherHeader = <>, - encode_iolist(Type, Version, Data, CS, CompS, CipherS, Seq + 1, + encode_fragments(Type, Version, Data, CS, CompS, CipherS, Seq + 1, [[CipherHeader, CipherFragment] | CipherFragments]); -encode_iolist(Type, Version, [Text|Data], +encode_fragments(Type, Version, [Text|Data], #{current_write := #{security_parameters := #security_parameters{compression_algorithm = CompAlg, mac_algorithm = MacAlgorithm} = SecPars, @@ -564,35 +564,49 @@ encode_iolist(Type, Version, [Text|Data], Length = byte_size(CipherFragment), {MajVer, MinVer} = Version, CipherHeader = <>, - encode_iolist(Type, Version, Data, CS, CompS, CipherS, Seq + 1, + encode_fragments(Type, Version, Data, CS, CompS, CipherS, Seq + 1, [[CipherHeader, CipherFragment] | CipherFragments]); -encode_iolist(_Type, _Version, _Data, CS, _CompS, _CipherS, _Seq, _CipherFragments) -> +encode_fragments(_Type, _Version, _Data, CS, _CompS, _CipherS, _Seq, _CipherFragments) -> exit({cs, CS}). %%-------------------------------------------------------------------- %% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are %% not vulnerable to this attack. -split_bin(<>, Version, BCA, one_n_minus_one) when - BCA =/= ?RC4 andalso ({3, 1} == Version orelse - {3, 0} == Version) -> - [[FirstByte]|do_split_bin(Rest)]; +split_iovec([<>|Data], Version, BCA, one_n_minus_one) + when (BCA =/= ?RC4) andalso ({3, 1} == Version orelse + {3, 0} == Version) -> + [[FirstByte]|split_iovec([Rest|Data])]; %% 0/n splitting countermeasure for clients that are incompatible with 1/n-1 %% splitting. -split_bin(Bin, Version, BCA, zero_n) when - BCA =/= ?RC4 andalso ({3, 1} == Version orelse - {3, 0} == Version) -> - [<<>>|do_split_bin(Bin)]; -split_bin(Bin, _, _, _) -> - do_split_bin(Bin). - -do_split_bin(<<>>) -> []; -do_split_bin(Bin) -> +split_iovec(Data, Version, BCA, zero_n) + when (BCA =/= ?RC4) andalso ({3, 1} == Version orelse + {3, 0} == Version) -> + [<<>>|split_iovec(Data)]; +split_iovec(Data, _Version, _BCA, _BeatMitigation) -> + split_iovec(Data). + +split_iovec([]) -> + []; +split_iovec(Data) -> + {Part,Rest} = split_iovec(Data, ?MAX_PLAIN_TEXT_LENGTH, []), + [Part|split_iovec(Rest)]. +%% +split_iovec([Bin|Data], Size, Acc) -> case Bin of - <> -> - [Chunk|do_split_bin(Rest)]; - _ -> - [Bin] - end. + <> -> + {lists:reverse(Acc, [Last]), + case Rest of + <<>> -> + Data; + <<_/binary>> -> + [Rest|Data] + end}; + <<_/binary>> -> + split_iovec(Data, Size - byte_size(Bin), [Bin|Acc]) + end; +split_iovec([], _Size, Acc) -> + {lists:reverse(Acc),[]}. + %%-------------------------------------------------------------------- lowest_list_protocol_version(Ver, []) -> Ver; diff --git a/lib/ssl/src/tls_sender.erl b/lib/ssl/src/tls_sender.erl index 8ccb8e287f..feda0b8672 100644 --- a/lib/ssl/src/tls_sender.erl +++ b/lib/ssl/src/tls_sender.erl @@ -272,7 +272,7 @@ connection({call, From}, {dist_handshake_complete, _Node, DHandle}, []; Data -> [{next_event, internal, - {application_packets,{self(),undefined},Data}}] + {application_packets,{self(),undefined},erlang:iolist_to_iovec(Data)}}] end]}; connection(internal, {application_packets, From, Data}, StateData) -> send_application_data(Data, From, ?FUNCTION_NAME, StateData); @@ -294,11 +294,11 @@ connection(info, dist_data, #data{static = #static{dist_handle = DHandle}}) -> []; Data -> [{next_event, internal, - {application_packets,{self(),undefined},Data}}] + {application_packets,{self(),undefined},erlang:iolist_to_iovec(Data)}}] end}; connection(info, tick, StateData) -> consume_ticks(), - Data = <<0:32>>, % encode_packet(4, <<>>) + Data = [<<0:32>>], % encode_packet(4, <<>>) From = {self(), undefined}, send_application_data(Data, From, ?FUNCTION_NAME, StateData); connection(info, {send, From, Ref, Data}, _StateData) -> @@ -309,7 +309,7 @@ connection(info, {send, From, Ref, Data}, _StateData) -> From ! {Ref, ok}, {keep_state_and_data, [{next_event, {call, {self(), undefined}}, - {application_data, iolist_to_binary(Data)}}]}; + {application_data, erlang:iolist_to_iovec(Data)}}]}; ?HANDLE_COMMON. %%-------------------------------------------------------------------- @@ -432,9 +432,7 @@ send_application_data(Data, From, StateName, {next_state, handshake, StateData0, [{next_event, internal, {application_packets, From, Data}}]}; false -> - {Msgs, ConnectionStates} = - Connection:encode_data( - iolist_to_binary(Data), Version, ConnectionStates0), + {Msgs, ConnectionStates} = Connection:encode_data(Data, Version, ConnectionStates0), StateData = StateData0#data{connection_states = ConnectionStates}, case Connection:send(Transport, Socket, Msgs) of ok when DistHandle =/= undefined -> @@ -452,9 +450,9 @@ send_application_data(Data, From, StateName, encode_packet(Packet, Data) -> Len = iolist_size(Data), case Packet of - 1 when Len < (1 bsl 8) -> [<>,Data]; - 2 when Len < (1 bsl 16) -> [<>,Data]; - 4 when Len < (1 bsl 32) -> [<>,Data]; + 1 when Len < (1 bsl 8) -> [<>|Data]; + 2 when Len < (1 bsl 16) -> [<>|Data]; + 4 when Len < (1 bsl 32) -> [<>|Data]; N when N =:= 1; N =:= 2; N =:= 4 -> {error, {badarg, {packet_to_large, Len, (1 bsl (Packet bsl 3)) - 1}}}; @@ -498,11 +496,17 @@ dist_data(DHandle) -> none -> erlang:dist_ctrl_get_data_notification(DHandle), []; - Data -> - %% This is encode_packet(4, Data) without Len check - %% since the emulator will always deliver a Data - %% smaller than 4 GB, and the distribution will - %% therefore always have to use {packet,4} + %% This is encode_packet(4, Data) without Len check + %% since the emulator will always deliver a Data + %% smaller than 4 GB, and the distribution will + %% therefore always have to use {packet,4} + Data when is_binary(Data) -> + Len = byte_size(Data), + [<>,Data|dist_data(DHandle)]; + [BA,BB] = Data -> + Len = byte_size(BA) + byte_size(BB), + [<>,Data|dist_data(DHandle)]; + Data when is_list(Data) -> Len = iolist_size(Data), [<>,Data|dist_data(DHandle)] end. -- cgit v1.2.3