1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2013-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19 20%%---------------------------------------------------------------------- 21%% Purpose: Help funtions for handling the DTLS (specific parts of) 22%%% SSL/TLS/DTLS handshake protocol 23%%---------------------------------------------------------------------- 24-module(dtls_handshake). 25 26-include("dtls_connection.hrl"). 27-include("dtls_handshake.hrl"). 28-include("dtls_record.hrl"). 29-include("ssl_internal.hrl"). 30-include("ssl_alert.hrl"). 31 32%% Handshake handling 33-export([client_hello/7, client_hello/9, cookie/4, hello/5, hello/4, 34 hello_verify_request/2]). 35 36%% Handshake encoding 37-export([fragment_handshake/2, encode_handshake/3]). 38 39%% Handshake decodeing 40-export([get_dtls_handshake/4]). 41 42-type dtls_handshake() :: #client_hello{} | #hello_verify_request{} | 43 ssl_handshake:ssl_handshake(). 44 45%%==================================================================== 46%% Handshake handling 47%%==================================================================== 48%%-------------------------------------------------------------------- 49-spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(), 50 ssl_options(), binary(), boolean(), [der_cert()]) -> 51 #client_hello{}. 52%% 53%% Description: Creates a client hello message. 54%%-------------------------------------------------------------------- 55client_hello(Host, Port, ConnectionStates, SslOpts, 56 Id, Renegotiation, OwnCerts) -> 57 %% First client hello (two sent in DTLS ) uses empty Cookie 58 client_hello(Host, Port, <<>>, ConnectionStates, SslOpts, 59 Id, Renegotiation, OwnCerts, undefined). 60 61%%-------------------------------------------------------------------- 62-spec client_hello(ssl:host(), inet:port_number(), term(), ssl_record:connection_states(), 63 ssl_options(), binary(),boolean(), [der_cert()], binary() | undefined) -> 64 #client_hello{}. 65%% 66%% Description: Creates a client hello message. 67%%-------------------------------------------------------------------- 68client_hello(_Host, _Port, Cookie, ConnectionStates, 69 #{versions := Versions, 70 ciphers := UserSuites, 71 fallback := Fallback} = SslOpts, 72 Id, Renegotiation, _OwnCert, OcspNonce) -> 73 Version = dtls_record:highest_protocol_version(Versions), 74 Pending = ssl_record:pending_connection_state(ConnectionStates, read), 75 SecParams = maps:get(security_parameters, Pending), 76 TLSVersion = dtls_v1:corresponding_tls_version(Version), 77 CipherSuites = ssl_handshake:available_suites(UserSuites, TLSVersion), 78 79 Extensions = ssl_handshake:client_hello_extensions(TLSVersion, 80 CipherSuites, 81 SslOpts, 82 ConnectionStates, 83 Renegotiation, 84 undefined, 85 undefined, 86 OcspNonce), 87 #client_hello{session_id = Id, 88 client_version = Version, 89 cipher_suites = 90 ssl_handshake:cipher_suites(CipherSuites, 91 Renegotiation, Fallback), 92 compression_methods = ssl_record:compressions(), 93 random = SecParams#security_parameters.client_random, 94 cookie = Cookie, 95 extensions = Extensions 96 }. 97 98hello(#server_hello{server_version = Version, random = Random, 99 cipher_suite = CipherSuite, 100 compression_method = Compression, 101 session_id = SessionId, extensions = HelloExt}, 102 #{versions := SupportedVersions} = SslOpt, 103 ConnectionStates0, Renegotiation, OldId) -> 104 IsNew = ssl_session:is_new(OldId, SessionId), 105 case dtls_record:is_acceptable_version(Version, SupportedVersions) of 106 true -> 107 handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, 108 Compression, HelloExt, SslOpt, 109 ConnectionStates0, Renegotiation, IsNew); 110 false -> 111 ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) 112 end. 113hello(#client_hello{client_version = ClientVersion} = Hello, 114 #{versions := Versions} = SslOpts, 115 Info, Renegotiation) -> 116 Version = ssl_handshake:select_version(dtls_record, ClientVersion, Versions), 117 handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation). 118 119cookie(Key, Address, Port, #client_hello{client_version = {Major, Minor}, 120 random = Random, 121 session_id = SessionId, 122 cipher_suites = CipherSuites, 123 compression_methods = CompressionMethods}) -> 124 CookieData = [address_to_bin(Address, Port), 125 <<?BYTE(Major), ?BYTE(Minor)>>, 126 Random, SessionId, CipherSuites, CompressionMethods], 127 crypto:mac(hmac, sha, Key, CookieData). 128%%-------------------------------------------------------------------- 129-spec hello_verify_request(binary(), ssl_record:ssl_version()) -> #hello_verify_request{}. 130%% 131%% Description: Creates a hello verify request message sent by server to 132%% verify client 133%%-------------------------------------------------------------------- 134hello_verify_request(Cookie, Version) -> 135 #hello_verify_request{protocol_version = Version, cookie = Cookie}. 136 137%%-------------------------------------------------------------------- 138%%% Handshake encoding 139%%-------------------------------------------------------------------- 140 141fragment_handshake(Bin, _) when is_binary(Bin)-> 142 %% This is the change_cipher_spec not a "real handshake" but part of the flight 143 Bin; 144fragment_handshake([MsgType, Len, Seq, _, Len, Bin], Size) -> 145 Bins = bin_fragments(Bin, Size), 146 handshake_fragments(MsgType, Seq, Len, Bins, []). 147encode_handshake(Handshake, Version, Seq) -> 148 {MsgType, Bin} = enc_handshake(Handshake, Version), 149 Len = byte_size(Bin), 150 [MsgType, ?uint24(Len), ?uint16(Seq), ?uint24(0), ?uint24(Len), Bin]. 151 152%%-------------------------------------------------------------------- 153%%% Handshake decodeing 154%%-------------------------------------------------------------------- 155 156%%-------------------------------------------------------------------- 157-spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}, ssl_options()) -> 158 {[dtls_handshake()], #protocol_buffers{}}. 159%% 160%% Description: Given buffered and new data from dtls_record, collects 161%% and returns it as a list of handshake messages, also returns 162%% possible leftover data in the new "protocol_buffers". 163%%-------------------------------------------------------------------- 164get_dtls_handshake(Version, Fragment, ProtocolBuffers, Options) -> 165 handle_fragments(Version, Fragment, ProtocolBuffers, Options, []). 166 167%%-------------------------------------------------------------------- 168%%% Internal functions 169%%-------------------------------------------------------------------- 170handle_client_hello(Version, 171 #client_hello{session_id = SugesstedId, 172 cipher_suites = CipherSuites, 173 compression_methods = Compressions, 174 random = Random, 175 extensions = HelloExt}, 176 #{versions := Versions, 177 signature_algs := SupportedHashSigns, 178 eccs := SupportedECCs, 179 honor_ecc_order := ECCOrder} = SslOpts, 180 {SessIdTracker, Session0, ConnectionStates0, OwnCerts, _}, 181 Renegotiation) -> 182 OwnCert = ssl_handshake:select_own_cert(OwnCerts), 183 case dtls_record:is_acceptable_version(Version, Versions) of 184 true -> 185 Curves = maps:get(elliptic_curves, HelloExt, undefined), 186 ClientHashSigns = maps:get(signature_algs, HelloExt, undefined), 187 TLSVersion = dtls_v1:corresponding_tls_version(Version), 188 AvailableHashSigns = ssl_handshake:available_signature_algs( 189 ClientHashSigns, SupportedHashSigns, OwnCert,TLSVersion), 190 ECCCurve = ssl_handshake:select_curve(Curves, SupportedECCs, ECCOrder), 191 {Type, #session{cipher_suite = CipherSuite} = Session1} 192 = ssl_handshake:select_session(SugesstedId, CipherSuites, 193 AvailableHashSigns, Compressions, 194 SessIdTracker, Session0#session{ecc = ECCCurve}, TLSVersion, 195 SslOpts, OwnCert), 196 case CipherSuite of 197 no_suite -> 198 ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); 199 _ -> 200 #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite), 201 case ssl_handshake:select_hashsign({ClientHashSigns, undefined}, OwnCert, KeyExAlg, 202 SupportedHashSigns, TLSVersion) of 203 #alert{} = Alert -> 204 Alert; 205 HashSign -> 206 handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, 207 SslOpts, Session1, ConnectionStates0, 208 Renegotiation, HashSign) 209 end 210 end; 211 false -> 212 ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) 213 end. 214 215handle_client_hello_extensions(Version, Type, Random, CipherSuites, 216 HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation, HashSign) -> 217 try ssl_handshake:handle_client_hello_extensions(dtls_record, Random, CipherSuites, 218 HelloExt, dtls_v1:corresponding_tls_version(Version), 219 SslOpts, Session0, 220 ConnectionStates0, Renegotiation, 221 Session0#session.is_resumable) of 222 {Session, ConnectionStates, Protocol, ServerHelloExt} -> 223 {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign} 224 catch throw:Alert -> 225 Alert 226 end. 227 228handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, 229 Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation, IsNew) -> 230 try ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite, 231 Compression, HelloExt, 232 dtls_v1:corresponding_tls_version(Version), 233 SslOpt, ConnectionStates0, Renegotiation, IsNew) of 234 {ConnectionStates, ProtoExt, Protocol, OcspState} -> 235 {Version, SessionId, ConnectionStates, ProtoExt, Protocol, OcspState} 236 catch throw:Alert -> 237 Alert 238 end. 239 240%%-------------------------------------------------------------------- 241 242enc_handshake(#hello_verify_request{protocol_version = {Major, Minor}, 243 cookie = Cookie}, _Version) -> 244 CookieLength = byte_size(Cookie), 245 {?HELLO_VERIFY_REQUEST, <<?BYTE(Major), ?BYTE(Minor), 246 ?BYTE(CookieLength), 247 Cookie:CookieLength/binary>>}; 248enc_handshake(#hello_request{}, _Version) -> 249 {?HELLO_REQUEST, <<>>}; 250enc_handshake(#client_hello{client_version = {Major, Minor}, 251 random = Random, 252 session_id = SessionID, 253 cookie = Cookie, 254 cipher_suites = CipherSuites, 255 compression_methods = CompMethods, 256 extensions = HelloExtensions}, _Version) -> 257 SIDLength = byte_size(SessionID), 258 CookieLength = byte_size(Cookie), 259 BinCompMethods = list_to_binary(CompMethods), 260 CmLength = byte_size(BinCompMethods), 261 BinCipherSuites = list_to_binary(CipherSuites), 262 CsLength = byte_size(BinCipherSuites), 263 ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions, 264 dtls_v1:corresponding_tls_version({Major, Minor})), 265 266 {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, 267 ?BYTE(SIDLength), SessionID/binary, 268 ?BYTE(CookieLength), Cookie/binary, 269 ?UINT16(CsLength), BinCipherSuites/binary, 270 ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>}; 271enc_handshake(#server_hello{} = HandshakeMsg, Version) -> 272 {Type, <<?BYTE(Major), ?BYTE(Minor), Rest/binary>>} = 273 ssl_handshake:encode_handshake(HandshakeMsg, Version), 274 {DTLSMajor, DTLSMinor} = dtls_v1:corresponding_dtls_version({Major, Minor}), 275 {Type, <<?BYTE(DTLSMajor), ?BYTE(DTLSMinor), Rest/binary>>}; 276enc_handshake(HandshakeMsg, Version) -> 277 ssl_handshake:encode_handshake(HandshakeMsg, dtls_v1:corresponding_tls_version(Version)). 278 279handshake_bin(#handshake_fragment{ 280 type = Type, 281 length = Len, 282 message_seq = Seq, 283 fragment_length = Len, 284 fragment_offset = 0, 285 fragment = Fragment}) -> 286 handshake_bin(Type, Len, Seq, Fragment). 287handshake_bin(Type, Length, Seq, FragmentData) -> 288 <<?BYTE(Type), ?UINT24(Length), 289 ?UINT16(Seq), ?UINT24(0), ?UINT24(Length), 290 FragmentData:Length/binary>>. 291 292bin_fragments(Bin, Size) -> 293 bin_fragments(Bin, byte_size(Bin), Size, 0, []). 294bin_fragments(Bin, BinSize, FragSize, Offset, Fragments) -> 295 case (BinSize - Offset - FragSize) > 0 of 296 true -> 297 Frag = binary:part(Bin, {Offset, FragSize}), 298 bin_fragments(Bin, BinSize, FragSize, Offset + FragSize, [{Frag, Offset} | Fragments]); 299 false -> 300 Frag = binary:part(Bin, {Offset, BinSize-Offset}), 301 lists:reverse([{Frag, Offset} | Fragments]) 302 end. 303 304handshake_fragments(_, _, _, [], Acc) -> 305 lists:reverse(Acc); 306handshake_fragments(MsgType, Seq, Len, [{Bin, Offset} | Bins], Acc) -> 307 FragLen = byte_size(Bin), 308 handshake_fragments(MsgType, Seq, Len, Bins, 309 [<<?BYTE(MsgType), Len/binary, Seq/binary, ?UINT24(Offset), 310 ?UINT24(FragLen), Bin/binary>> | Acc]). 311 312address_to_bin({A,B,C,D}, Port) -> 313 <<0:80,16#ffff:16,A,B,C,D,Port:16>>; 314address_to_bin({A,B,C,D,E,F,G,H}, Port) -> 315 <<A:16,B:16,C:16,D:16,E:16,F:16,G:16,H:16,Port:16>>. 316 317%%-------------------------------------------------------------------- 318 319handle_fragments(Version, FragmentData, Buffers0, Options, Acc) -> 320 Fragments = decode_handshake_fragments(FragmentData), 321 do_handle_fragments(Version, Fragments, Buffers0, Options, Acc). 322 323do_handle_fragments(_, [], Buffers, _Options, Acc) -> 324 {lists:reverse(Acc), Buffers}; 325do_handle_fragments(Version, [Fragment | Fragments], Buffers0, #{log_level := LogLevel} = Options, Acc) -> 326 case reassemble(Version, Fragment, Buffers0) of 327 {more_data, Buffers} when Fragments == [] -> 328 {lists:reverse(Acc), Buffers}; 329 {more_data, Buffers} -> 330 do_handle_fragments(Version, Fragments, Buffers, Options, Acc); 331 {{Handshake, _} = HsPacket, Buffers} -> 332 ssl_logger:debug(LogLevel, inbound, 'handshake', Handshake), 333 do_handle_fragments(Version, Fragments, Buffers, Options, [HsPacket | Acc]) 334 end. 335 336decode_handshake(Version, <<?BYTE(Type), Bin/binary>>) -> 337 decode_handshake(Version, Type, Bin). 338 339decode_handshake(_, ?HELLO_REQUEST, <<>>) -> 340 #hello_request{}; 341decode_handshake(Version, ?CLIENT_HELLO, <<?UINT24(_), ?UINT16(_), 342 ?UINT24(_), ?UINT24(_), 343 ?BYTE(Major), ?BYTE(Minor), Random:32/binary, 344 ?BYTE(SID_length), Session_ID:SID_length/binary, 345 ?BYTE(CookieLength), Cookie:CookieLength/binary, 346 ?UINT16(Cs_length), CipherSuites:Cs_length/binary, 347 ?BYTE(Cm_length), Comp_methods:Cm_length/binary, 348 Extensions/binary>>) -> 349 TLSVersion = dtls_v1:corresponding_tls_version(Version), 350 LegacyVersion = dtls_v1:corresponding_tls_version({Major, Minor}), 351 Exts = ssl_handshake:decode_vector(Extensions), 352 DecodedExtensions = ssl_handshake:decode_hello_extensions(Exts, TLSVersion, LegacyVersion, client), 353 354 #client_hello{ 355 client_version = {Major,Minor}, 356 random = Random, 357 cookie = Cookie, 358 session_id = Session_ID, 359 cipher_suites = ssl_handshake:decode_suites('2_bytes', CipherSuites), 360 compression_methods = Comp_methods, 361 extensions = DecodedExtensions 362 }; 363decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, <<?UINT24(_), ?UINT16(_), 364 ?UINT24(_), ?UINT24(_), 365 ?BYTE(Major), ?BYTE(Minor), 366 ?BYTE(CookieLength), 367 Cookie:CookieLength/binary>>) -> 368 #hello_verify_request{protocol_version = {Major, Minor}, 369 cookie = Cookie}; 370decode_handshake(Version, Tag, <<?UINT24(_), ?UINT16(_), 371 ?UINT24(_), ?UINT24(_), Msg/binary>>) -> 372 %% DTLS specifics stripped 373 decode_tls_handshake(Version, Tag, Msg). 374 375decode_tls_handshake(Version, Tag, Msg) -> 376 TLSVersion = dtls_v1:corresponding_tls_version(Version), 377 ssl_handshake:decode_handshake(TLSVersion, Tag, Msg). 378 379decode_handshake_fragments(<<>>) -> 380 [<<>>]; 381decode_handshake_fragments(<<?BYTE(Type), ?UINT24(Length), 382 ?UINT16(MessageSeq), 383 ?UINT24(FragmentOffset), ?UINT24(FragmentLength), 384 Fragment:FragmentLength/binary, Rest/binary>>) -> 385 [#handshake_fragment{type = Type, 386 length = Length, 387 message_seq = MessageSeq, 388 fragment_offset = FragmentOffset, 389 fragment_length = FragmentLength, 390 fragment = Fragment} | decode_handshake_fragments(Rest)]. 391 392reassemble(Version, #handshake_fragment{message_seq = Seq} = Fragment, 393 #protocol_buffers{dtls_handshake_next_seq = Seq, 394 dtls_handshake_next_fragments = Fragments0, 395 dtls_handshake_later_fragments = LaterFragments0} = 396 Buffers0)-> 397 case reassemble_fragments(Fragment, Fragments0) of 398 {more_data, Fragments} -> 399 {more_data, Buffers0#protocol_buffers{dtls_handshake_next_fragments = Fragments}}; 400 {raw, RawHandshake} -> 401 Handshake = decode_handshake(Version, RawHandshake), 402 {NextFragments, LaterFragments} = next_fragments(LaterFragments0), 403 {{Handshake, RawHandshake}, Buffers0#protocol_buffers{dtls_handshake_next_seq = Seq + 1, 404 dtls_handshake_next_fragments = NextFragments, 405 dtls_handshake_later_fragments = LaterFragments}} 406 end; 407reassemble(_, #handshake_fragment{message_seq = FragSeq} = Fragment, 408 #protocol_buffers{dtls_handshake_next_seq = Seq, 409 dtls_handshake_later_fragments = LaterFragments} 410 = Buffers0) when FragSeq > Seq-> 411 {more_data, 412 Buffers0#protocol_buffers{dtls_handshake_later_fragments = [Fragment | LaterFragments]}}; 413reassemble(_, _, Buffers) -> 414 %% Disregard fragments FragSeq < Seq 415 {more_data, Buffers}. 416 417reassemble_fragments(Current, Fragments0) -> 418 [Frag1 | Frags] = lists:keysort(#handshake_fragment.fragment_offset, [Current | Fragments0]), 419 [Fragment | _] = Fragments = merge_fragment(Frag1, Frags), 420 case is_complete_handshake(Fragment) of 421 true -> 422 {raw, handshake_bin(Fragment)}; 423 false -> 424 {more_data, Fragments} 425 end. 426 427merge_fragment(Frag0, []) -> 428 [Frag0]; 429merge_fragment(Frag0, [Frag1 | Rest]) -> 430 case merge_fragments(Frag0, Frag1) of 431 [_|_] = Frags -> 432 Frags ++ Rest; 433 Frag -> 434 merge_fragment(Frag, Rest) 435 end. 436 437 438%% Duplicate (fully contained fragment) 439%% 2,5 _ _ P P P P P 440%% 2,5 _ _ C C C C C 441merge_fragments(#handshake_fragment{ 442 fragment_offset = PreviousOffSet, 443 fragment_length = PreviousLen, 444 fragment = PreviousData 445 } = Previous, 446 #handshake_fragment{ 447 fragment_offset = PreviousOffSet, 448 fragment_length = PreviousLen, 449 fragment = PreviousData}) -> 450 Previous; 451 452%% Duplicate (fully contained fragment) 453%% 2,5 _ _ P P P P P 454%% 2,2 _ _ C C 455%% 0,3 X X X 456%% 5,3 _ _ _ _ _ X X X 457merge_fragments(#handshake_fragment{ 458 fragment_offset = PreviousOffset, 459 fragment_length = PreviousLen 460 } = Previous, 461 #handshake_fragment{ 462 fragment_offset = CurrentOffset, 463 fragment_length = CurrentLen}) 464 when PreviousOffset =< CurrentOffset andalso 465 CurrentOffset =< PreviousOffset + PreviousLen andalso 466 CurrentOffset + CurrentLen =< PreviousOffset + PreviousLen -> 467 Previous; 468 469%% Fully overlapping fragments 470%% 2,5 _ _ P P P P P 471%% 0,8 C C C C C C C C 472merge_fragments(#handshake_fragment{ 473 fragment_offset = PreviousOffset, 474 fragment_length = PreviousLen 475 }, 476 #handshake_fragment{ 477 fragment_offset = CurrentOffset, 478 fragment_length = CurrentLen} = Current) 479 when CurrentOffset =< PreviousOffset andalso 480 CurrentOffset + CurrentLen >= PreviousOffset + PreviousLen -> 481 Current; 482 483%% Overlapping fragments 484%% 2,5 _ _ P P P P P 485%% 0,3 C C C 486merge_fragments(#handshake_fragment{ 487 fragment_offset = PreviousOffset, 488 fragment_length = PreviousLen, 489 fragment = PreviousData 490 } = Previous, 491 #handshake_fragment{ 492 fragment_offset = CurrentOffset, 493 fragment_length = CurrentLen, 494 fragment = CurrentData}) 495 when CurrentOffset < PreviousOffset andalso 496 CurrentOffset + CurrentLen < PreviousOffset + PreviousLen -> 497 NewDataLen = PreviousOffset - CurrentOffset, 498 <<NewData:NewDataLen/binary, _/binary>> = CurrentData, 499 Previous#handshake_fragment{ 500 fragment_length = PreviousLen + NewDataLen, 501 fragment = <<NewData/binary, PreviousData/binary>> 502 }; 503 504%% Overlapping fragments 505%% 2,5 _ _ P P P P P 506%% 5,3 _ _ _ _ _ C C C 507merge_fragments(#handshake_fragment{ 508 fragment_offset = PreviousOffset, 509 fragment_length = PreviousLen, 510 fragment = PreviousData 511 } = Previous, 512 #handshake_fragment{ 513 fragment_offset = CurrentOffset, 514 fragment_length = CurrentLen, 515 fragment = CurrentData}) 516 when CurrentOffset > PreviousOffset andalso 517 CurrentOffset < PreviousOffset + PreviousLen -> 518 NewDataLen = CurrentOffset + CurrentLen - (PreviousOffset + PreviousLen), 519 DropLen = CurrentLen - NewDataLen, 520 <<_:DropLen/binary, NewData/binary>> = CurrentData, 521 Previous#handshake_fragment{ 522 fragment_length = PreviousLen + NewDataLen, 523 fragment = <<PreviousData/binary, NewData/binary>> 524 }; 525 526%% Adjacent fragments 527%% 2,5 _ _ P P P P P 528%% 7,3 _ _ _ _ _ _ _ C C C 529merge_fragments(#handshake_fragment{ 530 fragment_offset = PreviousOffset, 531 fragment_length = PreviousLen, 532 fragment = PreviousData 533 } = Previous, 534 #handshake_fragment{ 535 fragment_offset = CurrentOffset, 536 fragment_length = CurrentLen, 537 fragment = CurrentData}) 538 when CurrentOffset =:= PreviousOffset + PreviousLen -> 539 Previous#handshake_fragment{ 540 fragment_length = PreviousLen + CurrentLen, 541 fragment = <<PreviousData/binary, CurrentData/binary>> 542 }; 543 544%% Adjacent fragments 545%% 2,5 _ _ P P P P P 546%% 0,2 C C 547merge_fragments(#handshake_fragment{ 548 fragment_offset = PreviousOffset, 549 fragment_length = PreviousLen, 550 fragment = PreviousData 551 } = Previous, 552 #handshake_fragment{ 553 fragment_offset = CurrentOffset, 554 fragment_length = CurrentLen, 555 fragment = CurrentData}) 556 when PreviousOffset =:= CurrentOffset + CurrentLen -> 557 Previous#handshake_fragment{ 558 fragment_length = PreviousLen + CurrentLen, 559 fragment = <<CurrentData/binary, PreviousData/binary>> 560 }; 561 562%% No merge there is a gap 563%% 3,5 _ _ _ P P P P 564%% 0,2 C C 565merge_fragments(Previous, Current) -> 566 [Previous, Current]. 567 568next_fragments(LaterFragments) -> 569 case lists:keysort(#handshake_fragment.message_seq, LaterFragments) of 570 [] -> 571 {[], []}; 572 [#handshake_fragment{message_seq = Seq} | _] = Fragments -> 573 split_frags(Fragments, Seq, []) 574 end. 575 576split_frags([#handshake_fragment{message_seq = Seq} = Frag | Rest], Seq, Acc) -> 577 split_frags(Rest, Seq, [Frag | Acc]); 578split_frags(Frags, _, Acc) -> 579 {lists:reverse(Acc), Frags}. 580 581is_complete_handshake(#handshake_fragment{length = Length, fragment_length = Length}) -> 582 true; 583is_complete_handshake(_) -> 584 false. 585 586 587 588 589 590