xref: /openssl/util/perl/TLSProxy/Message.pm (revision 80008d4a)
1# Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
2#
3# Licensed under the Apache License 2.0 (the "License").  You may not use
4# this file except in compliance with the License.  You can obtain a copy
5# in the file LICENSE in the source distribution or at
6# https://www.openssl.org/source/license.html
7
8use strict;
9
10package TLSProxy::Message;
11
12use TLSProxy::Alert;
13
14use constant DTLS_MESSAGE_HEADER_LENGTH => 12;
15use constant TLS_MESSAGE_HEADER_LENGTH => 4;
16
17#Message types
18use constant {
19    MT_HELLO_REQUEST => 0,
20    MT_CLIENT_HELLO => 1,
21    MT_SERVER_HELLO => 2,
22    MT_HELLO_VERIFY_REQUEST => 3,
23    MT_NEW_SESSION_TICKET => 4,
24    MT_ENCRYPTED_EXTENSIONS => 8,
25    MT_CERTIFICATE => 11,
26    MT_SERVER_KEY_EXCHANGE => 12,
27    MT_CERTIFICATE_REQUEST => 13,
28    MT_SERVER_HELLO_DONE => 14,
29    MT_CERTIFICATE_VERIFY => 15,
30    MT_CLIENT_KEY_EXCHANGE => 16,
31    MT_FINISHED => 20,
32    MT_CERTIFICATE_STATUS => 22,
33    MT_COMPRESSED_CERTIFICATE => 25,
34    MT_NEXT_PROTO => 67
35};
36
37#Alert levels
38use constant {
39    AL_LEVEL_WARN => 1,
40    AL_LEVEL_FATAL => 2
41};
42
43#Alert descriptions
44use constant {
45    AL_DESC_CLOSE_NOTIFY => 0,
46    AL_DESC_UNEXPECTED_MESSAGE => 10,
47    AL_DESC_BAD_RECORD_MAC => 20,
48    AL_DESC_ILLEGAL_PARAMETER => 47,
49    AL_DESC_DECODE_ERROR => 50,
50    AL_DESC_PROTOCOL_VERSION => 70,
51    AL_DESC_NO_RENEGOTIATION => 100,
52    AL_DESC_MISSING_EXTENSION => 109
53};
54
55my %message_type = (
56    MT_HELLO_REQUEST, "HelloRequest",
57    MT_CLIENT_HELLO, "ClientHello",
58    MT_SERVER_HELLO, "ServerHello",
59    MT_HELLO_VERIFY_REQUEST, "HelloVerifyRequest",
60    MT_NEW_SESSION_TICKET, "NewSessionTicket",
61    MT_ENCRYPTED_EXTENSIONS, "EncryptedExtensions",
62    MT_CERTIFICATE, "Certificate",
63    MT_SERVER_KEY_EXCHANGE, "ServerKeyExchange",
64    MT_CERTIFICATE_REQUEST, "CertificateRequest",
65    MT_SERVER_HELLO_DONE, "ServerHelloDone",
66    MT_CERTIFICATE_VERIFY, "CertificateVerify",
67    MT_CLIENT_KEY_EXCHANGE, "ClientKeyExchange",
68    MT_FINISHED, "Finished",
69    MT_CERTIFICATE_STATUS, "CertificateStatus",
70    MT_COMPRESSED_CERTIFICATE, "CompressedCertificate",
71    MT_NEXT_PROTO, "NextProto"
72);
73
74use constant {
75    EXT_SERVER_NAME => 0,
76    EXT_MAX_FRAGMENT_LENGTH => 1,
77    EXT_STATUS_REQUEST => 5,
78    EXT_SUPPORTED_GROUPS => 10,
79    EXT_EC_POINT_FORMATS => 11,
80    EXT_SRP => 12,
81    EXT_SIG_ALGS => 13,
82    EXT_USE_SRTP => 14,
83    EXT_ALPN => 16,
84    EXT_SCT => 18,
85    EXT_CLIENT_CERT_TYPE => 19,
86    EXT_SERVER_CERT_TYPE => 20,
87    EXT_PADDING => 21,
88    EXT_ENCRYPT_THEN_MAC => 22,
89    EXT_EXTENDED_MASTER_SECRET => 23,
90    EXT_COMPRESS_CERTIFICATE => 27,
91    EXT_SESSION_TICKET => 35,
92    EXT_KEY_SHARE => 51,
93    EXT_PSK => 41,
94    EXT_SUPPORTED_VERSIONS => 43,
95    EXT_COOKIE => 44,
96    EXT_PSK_KEX_MODES => 45,
97    EXT_POST_HANDSHAKE_AUTH => 49,
98    EXT_SIG_ALGS_CERT => 50,
99    EXT_RENEGOTIATE => 65281,
100    EXT_NPN => 13172,
101    EXT_CRYPTOPRO_BUG_EXTENSION => 0xfde8,
102    EXT_UNKNOWN => 0xfffe,
103    #Unknown extension that should appear last
104    EXT_FORCE_LAST => 0xffff
105};
106
107# SignatureScheme of TLS 1.3 from:
108# https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme
109# We have to manually grab the SHA224 equivalents from the old registry
110use constant {
111    SIG_ALG_RSA_PKCS1_SHA256 => 0x0401,
112    SIG_ALG_RSA_PKCS1_SHA384 => 0x0501,
113    SIG_ALG_RSA_PKCS1_SHA512 => 0x0601,
114    SIG_ALG_ECDSA_SECP256R1_SHA256 => 0x0403,
115    SIG_ALG_ECDSA_SECP384R1_SHA384 => 0x0503,
116    SIG_ALG_ECDSA_SECP521R1_SHA512 => 0x0603,
117    SIG_ALG_RSA_PSS_RSAE_SHA256 => 0x0804,
118    SIG_ALG_RSA_PSS_RSAE_SHA384 => 0x0805,
119    SIG_ALG_RSA_PSS_RSAE_SHA512 => 0x0806,
120    SIG_ALG_ED25519 => 0x0807,
121    SIG_ALG_ED448 => 0x0808,
122    SIG_ALG_RSA_PSS_PSS_SHA256 => 0x0809,
123    SIG_ALG_RSA_PSS_PSS_SHA384 => 0x080a,
124    SIG_ALG_RSA_PSS_PSS_SHA512 => 0x080b,
125    SIG_ALG_RSA_PKCS1_SHA1 => 0x0201,
126    SIG_ALG_ECDSA_SHA1 => 0x0203,
127    SIG_ALG_DSA_SHA1 => 0x0202,
128    SIG_ALG_DSA_SHA256 => 0x0402,
129    SIG_ALG_DSA_SHA384 => 0x0502,
130    SIG_ALG_DSA_SHA512 => 0x0602,
131    OSSL_SIG_ALG_RSA_PKCS1_SHA224 => 0x0301,
132    OSSL_SIG_ALG_DSA_SHA224 => 0x0302,
133    OSSL_SIG_ALG_ECDSA_SHA224 => 0x0303
134};
135
136use constant {
137    CIPHER_RSA_WITH_AES_128_CBC_SHA => 0x002f,
138    CIPHER_DHE_RSA_AES_128_SHA => 0x0033,
139    CIPHER_ADH_AES_128_SHA => 0x0034,
140    CIPHER_TLS13_AES_128_GCM_SHA256 => 0x1301,
141    CIPHER_TLS13_AES_256_GCM_SHA384 => 0x1302
142};
143
144use constant {
145    CLIENT => 0,
146    SERVER => 1
147};
148
149my $payload = "";
150my $messlen = -1;
151my $mt;
152my $startoffset = -1;
153my $server = 0;
154my $success = 0;
155my $end = 0;
156my @message_rec_list = ();
157my @message_frag_lens = ();
158my $ciphersuite = 0;
159my $successondata = 0;
160my $alert;
161
162sub clear
163{
164    $payload = "";
165    $messlen = -1;
166    $startoffset = -1;
167    $server = 0;
168    $success = 0;
169    $end = 0;
170    $successondata = 0;
171    @message_rec_list = ();
172    @message_frag_lens = ();
173    $alert = undef;
174}
175
176#Class method to extract messages from a record
177sub get_messages
178{
179    my $class = shift;
180    my $serverin = shift;
181    my $record = shift;
182    my $isdtls = shift;
183    my @messages = ();
184    my $message;
185
186    @message_frag_lens = ();
187
188    if ($serverin != $server && length($payload) != 0) {
189        die "Changed peer, but we still have fragment data\n";
190    }
191    $server = $serverin;
192
193    if ($record->content_type == TLSProxy::Record::RT_CCS) {
194        if ($payload ne "") {
195            #We can't handle this yet
196            die "CCS received before message data complete\n";
197        }
198        if (!TLSProxy::Proxy->is_tls13()) {
199            if ($server) {
200                TLSProxy::Record->server_encrypting(1);
201            } else {
202                TLSProxy::Record->client_encrypting(1);
203            }
204        }
205    } elsif ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) {
206        if ($record->len == 0 || $record->len_real == 0) {
207            print "  Message truncated\n";
208        } else {
209            my $recoffset = 0;
210
211            if (length $payload > 0) {
212                #We are continuing processing a message started in a previous
213                #record. Add this record to the list associated with this
214                #message
215                push @message_rec_list, $record;
216
217                if ($messlen <= length($payload)) {
218                    #Shouldn't happen
219                    die "Internal error: invalid messlen: ".$messlen
220                        ." payload length:".length($payload)."\n";
221                }
222                if (length($payload) + $record->decrypt_len >= $messlen) {
223                    #We can complete the message with this record
224                    $recoffset = $messlen - length($payload);
225                    $payload .= substr($record->decrypt_data, 0, $recoffset);
226                    push @message_frag_lens, $recoffset;
227                    if ($isdtls) {
228                        # We must set $msgseq, $msgfrag, $msgfragoffs
229                        die "Internal error: cannot handle partial dtls messages\n"
230                    }
231                    $message = create_message($server, $mt,
232                        #$msgseq, $msgfrag, $msgfragoffs,
233                        0, 0, 0,
234                        $payload, $startoffset, $isdtls);
235                    push @messages, $message;
236
237                    $payload = "";
238                } else {
239                    #This is just part of the total message
240                    $payload .= $record->decrypt_data;
241                    $recoffset = $record->decrypt_len;
242                    push @message_frag_lens, $record->decrypt_len;
243                }
244                print "  Partial message data read: ".$recoffset." bytes\n";
245            }
246
247            while ($record->decrypt_len > $recoffset) {
248                #We are at the start of a new message
249                my $msgheaderlen = $isdtls ? DTLS_MESSAGE_HEADER_LENGTH
250                                           : TLS_MESSAGE_HEADER_LENGTH;
251                if ($record->decrypt_len - $recoffset < $msgheaderlen) {
252                    #Whilst technically probably valid we can't cope with this
253                    die "End of record in the middle of a message header\n";
254                }
255                @message_rec_list = ($record);
256                my $lenhi;
257                my $lenlo;
258                my $msgseq;
259                my $msgfrag;
260                my $msgfragoffs;
261                if ($isdtls) {
262                    my $msgfraghi;
263                    my $msgfraglo;
264                    my $msgfragoffshi;
265                    my $msgfragoffslo;
266                    ($mt, $lenhi, $lenlo, $msgseq, $msgfraghi, $msgfraglo, $msgfragoffshi, $msgfragoffslo) =
267                        unpack('CnCnnCnC', substr($record->decrypt_data, $recoffset));
268                    $msgfrag = ($msgfraghi << 8) | $msgfraglo;
269                    $msgfragoffs = ($msgfragoffshi << 8) | $msgfragoffslo;
270                } else {
271                    ($mt, $lenhi, $lenlo) =
272                        unpack('CnC', substr($record->decrypt_data, $recoffset));
273                }
274                $messlen = ($lenhi << 8) | $lenlo;
275                print "  Message type: $message_type{$mt}($mt)\n";
276                print "  Message Length: $messlen\n";
277                $startoffset = $recoffset;
278                $recoffset += $msgheaderlen;
279                $payload = "";
280
281                if ($recoffset <= $record->decrypt_len) {
282                    #Some payload data is present in this record
283                    if ($record->decrypt_len - $recoffset >= $messlen) {
284                        #We can complete the message with this record
285                        $payload .= substr($record->decrypt_data, $recoffset,
286                                           $messlen);
287                        $recoffset += $messlen;
288                        push @message_frag_lens, $messlen;
289                        $message = create_message($server, $mt, $msgseq,
290                                                  $msgfrag, $msgfragoffs,
291                                                  $payload, $startoffset, $isdtls);
292                        push @messages, $message;
293
294                        $payload = "";
295                    } else {
296                        #This is just part of the total message
297                        $payload .= substr($record->decrypt_data, $recoffset,
298                                           $record->decrypt_len - $recoffset);
299                        $recoffset = $record->decrypt_len;
300                        push @message_frag_lens, $recoffset;
301                    }
302                }
303            }
304        }
305    } elsif ($record->content_type == TLSProxy::Record::RT_APPLICATION_DATA) {
306        print "  [ENCRYPTED APPLICATION DATA]\n";
307        print "  [".$record->decrypt_data."]\n";
308
309        if ($successondata) {
310            $success = 1;
311            $end = 1;
312        }
313    } elsif ($record->content_type == TLSProxy::Record::RT_ALERT) {
314        my ($alertlev, $alertdesc) = unpack('CC', $record->decrypt_data);
315        print "  [$alertlev, $alertdesc]\n";
316        #A CloseNotify from the client indicates we have finished successfully
317        #(we assume)
318        if (!$end && !$server && $alertlev == AL_LEVEL_WARN
319            && $alertdesc == AL_DESC_CLOSE_NOTIFY) {
320            $success = 1;
321        }
322        #Fatal or close notify alerts end the test
323        if ($alertlev == AL_LEVEL_FATAL || $alertdesc == AL_DESC_CLOSE_NOTIFY) {
324            $end = 1;
325        }
326        $alert = TLSProxy::Alert->new(
327            $server,
328            $record->encrypted,
329            $alertlev,
330            $alertdesc);
331    }
332
333    return @messages;
334}
335
336#Function to work out which sub-class we need to create and then
337#construct it
338sub create_message
339{
340    my ($server, $mt, $msgseq, $msgfrag, $msgfragoffs, $data, $startoffset, $isdtls) = @_;
341    my $message;
342
343    if ($mt == MT_CLIENT_HELLO) {
344        $message = TLSProxy::ClientHello->new(
345            $isdtls,
346            $server,
347            $msgseq,
348            $msgfrag,
349            $msgfragoffs,
350            $data,
351            [@message_rec_list],
352            $startoffset,
353            [@message_frag_lens]
354        );
355        $message->parse();
356    } elsif ($mt == MT_SERVER_HELLO) {
357        $message = TLSProxy::ServerHello->new(
358            $isdtls,
359            $server,
360            $msgseq,
361            $msgfrag,
362            $msgfragoffs,
363            $data,
364            [@message_rec_list],
365            $startoffset,
366            [@message_frag_lens]
367        );
368        $message->parse();
369    } elsif ($mt == MT_HELLO_VERIFY_REQUEST) {
370        $message = TLSProxy::HelloVerifyRequest->new(
371            $isdtls,
372            $server,
373            $msgseq,
374            $msgfrag,
375            $msgfragoffs,
376            $data,
377            [@message_rec_list],
378            $startoffset,
379            [@message_frag_lens]
380        );
381        $message->parse();
382    } elsif ($mt == MT_ENCRYPTED_EXTENSIONS) {
383        $message = TLSProxy::EncryptedExtensions->new(
384            $isdtls,
385            $server,
386            $msgseq,
387            $msgfrag,
388            $msgfragoffs,
389            $data,
390            [@message_rec_list],
391            $startoffset,
392            [@message_frag_lens]
393        );
394        $message->parse();
395    } elsif ($mt == MT_CERTIFICATE) {
396        $message = TLSProxy::Certificate->new(
397            $isdtls,
398            $server,
399            $msgseq,
400            $msgfrag,
401            $msgfragoffs,
402            $data,
403            [@message_rec_list],
404            $startoffset,
405            [@message_frag_lens]
406        );
407        $message->parse();
408    } elsif ($mt == MT_CERTIFICATE_REQUEST) {
409        $message = TLSProxy::CertificateRequest->new(
410            $isdtls,
411            $server,
412            $msgseq,
413            $msgfrag,
414            $msgfragoffs,
415            $data,
416            [@message_rec_list],
417            $startoffset,
418            [@message_frag_lens]
419        );
420        $message->parse();
421    } elsif ($mt == MT_CERTIFICATE_VERIFY) {
422        $message = TLSProxy::CertificateVerify->new(
423            $isdtls,
424            $server,
425            $msgseq,
426            $msgfrag,
427            $msgfragoffs,
428            $data,
429            [@message_rec_list],
430            $startoffset,
431            [@message_frag_lens]
432        );
433        $message->parse();
434    } elsif ($mt == MT_SERVER_KEY_EXCHANGE) {
435        $message = TLSProxy::ServerKeyExchange->new(
436            $isdtls,
437            $server,
438            $msgseq,
439            $msgfrag,
440            $msgfragoffs,
441            $data,
442            [@message_rec_list],
443            $startoffset,
444            [@message_frag_lens]
445        );
446        $message->parse();
447    } elsif ($mt == MT_NEW_SESSION_TICKET) {
448        if ($isdtls) {
449            $message = TLSProxy::NewSessionTicket->new_dtls(
450                $server,
451                $msgseq,
452                $msgfrag,
453                $msgfragoffs,
454                $data,
455                [@message_rec_list],
456                $startoffset,
457                [@message_frag_lens]
458            );
459        } else {
460            $message = TLSProxy::NewSessionTicket->new(
461                $server,
462                $data,
463                [@message_rec_list],
464                $startoffset,
465                [@message_frag_lens]
466            );
467        }
468        $message->parse();
469    }  elsif ($mt == MT_NEXT_PROTO) {
470        $message = TLSProxy::NextProto->new(
471            $isdtls,
472            $server,
473            $msgseq,
474            $msgfrag,
475            $msgfragoffs,
476            $data,
477            [@message_rec_list],
478            $startoffset,
479            [@message_frag_lens]
480        );
481        $message->parse();
482    } else {
483        #Unknown message type
484        $message = TLSProxy::Message->new(
485            $isdtls,
486            $server,
487            $mt,
488            $msgseq,
489            $msgfrag,
490            $msgfragoffs,
491            $data,
492            [@message_rec_list],
493            $startoffset,
494            [@message_frag_lens]
495        );
496    }
497
498    return $message;
499}
500
501sub end
502{
503    my $class = shift;
504    return $end;
505}
506sub success
507{
508    my $class = shift;
509    return $success;
510}
511sub fail
512{
513    my $class = shift;
514    return !$success && $end;
515}
516
517sub alert
518{
519    return $alert;
520}
521
522sub new
523{
524    my $class = shift;
525    my ($isdtls,
526        $server,
527        $mt,
528        $msgseq,
529        $msgfrag,
530        $msgfragoffs,
531        $data,
532        $records,
533        $startoffset,
534        $message_frag_lens) = @_;
535
536    my $self = {
537        isdtls => $isdtls,
538        server => $server,
539        data => $data,
540        records => $records,
541        mt => $mt,
542        msgseq => $msgseq,
543        msgfrag => $msgfrag,
544        msgfragoffs => $msgfragoffs,
545        startoffset => $startoffset,
546        message_frag_lens => $message_frag_lens,
547        dupext => -1
548    };
549
550    return bless $self, $class;
551}
552
553sub ciphersuite
554{
555    my $class = shift;
556    if (@_) {
557      $ciphersuite = shift;
558    }
559    return $ciphersuite;
560}
561
562#Update all the underlying records with the modified data from this message
563#Note: Only supports TLSv1.3 and ETM encryption
564sub repack
565{
566    my $self = shift;
567    my $msgdata;
568
569    my $numrecs = $#{$self->records};
570
571    $self->set_message_contents();
572
573    my $lenlo = length($self->data) & 0xff;
574    my $lenhi = length($self->data) >> 8;
575
576    if ($self->{isdtls}) {
577        my $msgfraghi = $self->msgfrag >> 8;
578        my $msgfraglo = $self->msgfrag & 0xff;
579        my $msgfragoffshi = $self->msgfragoffs >> 8;
580        my $msgfragoffslo = $self->msgfragoffs & 0xff;
581
582        $msgdata = pack('CnCnnCnC', $self->mt, $lenhi, $lenlo, $self->msgseq,
583                                    $msgfraghi, $msgfraglo,
584                                    $msgfragoffshi, $msgfragoffslo).$self->data;
585    } else {
586        $msgdata = pack('CnC', $self->mt, $lenhi, $lenlo).$self->data;
587    }
588
589    if ($numrecs == 0) {
590        #The message is fully contained within one record
591        my ($rec) = @{$self->records};
592        my $recdata = $rec->decrypt_data;
593
594        my $old_length;
595        my $msg_header_len = $self->{isdtls} ? DTLS_MESSAGE_HEADER_LENGTH
596                                             : TLS_MESSAGE_HEADER_LENGTH;
597
598        # We use empty message_frag_lens to indicates that pre-repacking,
599        # the message wasn't present. The first fragment length doesn't include
600        # the TLS header, so we need to check and compute the right length.
601        if (@{$self->message_frag_lens}) {
602            $old_length = ${$self->message_frag_lens}[0] + $msg_header_len;
603        } else {
604            $old_length = 0;
605        }
606
607        my $prefix = substr($recdata, 0, $self->startoffset);
608        my $suffix = substr($recdata, $self->startoffset + $old_length);
609
610        $rec->decrypt_data($prefix.($msgdata).($suffix));
611        # TODO(openssl-team): don't keep explicit lengths.
612        # (If a length override is ever needed to construct invalid packets,
613        #  use an explicit override field instead.)
614        $rec->decrypt_len(length($rec->decrypt_data));
615        # Only support re-encryption for TLSv1.3 and ETM.
616        if ($rec->encrypted()) {
617            if (TLSProxy::Proxy->is_tls13()) {
618                #Add content type (1 byte) and 16 tag bytes
619                $rec->data($rec->decrypt_data
620                    .pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16));
621            } elsif ($rec->etm()) {
622                my $data = $rec->decrypt_data;
623                #Add padding
624                my $padval = length($data) % 16;
625                $padval = 15 - $padval;
626                for (0..$padval) {
627                    $data .= pack("C", $padval);
628                }
629
630                #Add MAC. Assumed to be 20 bytes
631                foreach my $macval (0..19) {
632                    $data .= pack("C", $macval);
633                }
634
635                if ($rec->version() >= TLSProxy::Record::VERS_TLS_1_1) {
636                    #Explicit IV
637                    $data = ("\0"x16).$data;
638                }
639                $rec->data($data);
640            } else {
641                die "Unsupported encryption: No ETM";
642            }
643        } else {
644            $rec->data($rec->decrypt_data);
645        }
646        $rec->len(length($rec->data));
647
648        #Update the fragment len in case we changed it above
649        ${$self->message_frag_lens}[0] = length($msgdata) - $msg_header_len;
650        return;
651    }
652
653    #Note we don't currently support changing a fragmented message length
654    my $recctr = 0;
655    my $datadone = 0;
656    foreach my $rec (@{$self->records}) {
657        my $recdata = $rec->decrypt_data;
658        if ($recctr == 0) {
659            #This is the first record
660            my $remainlen = length($recdata) - $self->startoffset;
661            $rec->data(substr($recdata, 0, $self->startoffset)
662                       .substr(($msgdata), 0, $remainlen));
663            $datadone += $remainlen;
664        } elsif ($recctr + 1 == $numrecs) {
665            #This is the last record
666            $rec->data(substr($msgdata, $datadone));
667        } else {
668            #This is a middle record
669            $rec->data(substr($msgdata, $datadone, length($rec->data)));
670            $datadone += length($rec->data);
671        }
672        $recctr++;
673    }
674}
675
676#To be overridden by sub-classes
677sub set_message_contents
678{
679}
680
681#Read only accessors
682sub server
683{
684    my $self = shift;
685    return $self->{server};
686}
687
688#Read/write accessors
689sub mt
690{
691    my $self = shift;
692    if (@_) {
693      $self->{mt} = shift;
694    }
695    return $self->{mt};
696}
697sub msgseq
698{
699    my $self = shift;
700    if (@_) {
701        $self->{msgseq} = shift;
702    }
703    return $self->{msgseq};
704}
705sub msgfrag
706{
707    my $self = shift;
708    if (@_) {
709        $self->{msgfrag} = shift;
710    }
711    return $self->{msgfrag};
712}
713sub msgfragoffs
714{
715    my $self = shift;
716    if (@_) {
717        $self->{msgfragoffs} = shift;
718    }
719    return $self->{msgfragoffs};
720}
721sub data
722{
723    my $self = shift;
724    if (@_) {
725      $self->{data} = shift;
726    }
727    return $self->{data};
728}
729sub records
730{
731    my $self = shift;
732    if (@_) {
733      $self->{records} = shift;
734    }
735    return $self->{records};
736}
737sub startoffset
738{
739    my $self = shift;
740    if (@_) {
741      $self->{startoffset} = shift;
742    }
743    return $self->{startoffset};
744}
745sub message_frag_lens
746{
747    my $self = shift;
748    if (@_) {
749      $self->{message_frag_lens} = shift;
750    }
751    return $self->{message_frag_lens};
752}
753sub encoded_length
754{
755    my $self = shift;
756    my $msg_header_len = $self->{isdtls} ? DTLS_MESSAGE_HEADER_LENGTH
757                                         : TLS_MESSAGE_HEADER_LENGTH;
758    return $msg_header_len + length($self->data);
759}
760sub dupext
761{
762    my $self = shift;
763    if (@_) {
764        $self->{dupext} = shift;
765    }
766    return $self->{dupext};
767}
768sub successondata
769{
770    my $class = shift;
771    if (@_) {
772        $successondata = shift;
773    }
774    return $successondata;
775}
7761;
777