55class DTLSReassembler
66{
77 /*
8- * Bounds the number of gaps tracked for one message. Each interior fragment splits a gap in two,
9- * so an unbounded list lets single-byte fragments at alternating offsets take a 32KiB message to
10- * 16K gaps, at a cost quadratic in the message length - seconds of CPU per message_seq in the
11- * flight, spent before anything about the peer has been verified. A cooperating peer fragments
12- * to the path MTU, so its gap count tracks its fragment count: a 1MiB message in 516-byte
13- * fragments arriving in fully random order peaks at 536 gaps, hence one gap per 512 bytes with a
14- * floor for small messages. At the cap a fragment that would split a gap is ignored for that gap
15- * - and not recorded as received, so a byte already held is still never overwritten - leaving the
16- * peer's normal retransmission of the flight to complete the message once the gaps close.
8+ * Bounds the number of gaps tracked for one message.
9+ * <p>
10+ * Limits the amount of fragmentation a malicious peer can cause. While at the limit a fragment that would
11+ * split a range is ignored; retransmission is then relied on to complete the message.
1712 */
18- private static final int MIN_MAX_MISSING_RANGES = 1024 ;
13+ private static final int MIN_MISSING_RANGES_LIMIT = 64 ;
1914
2015 /*
2116 * No 'final' modifiers so that it works in earlier JDKs
@@ -30,7 +25,7 @@ class DTLSReassembler
3025 {
3126 this .msg_type = msg_type ;
3227 this .body = new byte [length ];
33- this .maxMissingRanges = Math .max (MIN_MAX_MISSING_RANGES , length / 512 );
28+ this .maxMissingRanges = Math .max (MIN_MISSING_RANGES_LIMIT , length / 1024 );
3429 this .missing .addElement (new Range (0 , length ));
3530 }
3631
@@ -54,80 +49,74 @@ void contributeFragment(short msg_type, int length, byte[] buf, int off, int fra
5449 return ;
5550 }
5651
52+ // NOTE: Empty messages still require an empty fragment to complete it
5753 if (fragment_length == 0 )
5854 {
59- // NOTE: Empty messages still require an empty fragment to complete it
60- if (fragment_offset == 0 && !missing .isEmpty ())
55+ if (fragment_offset == 0 && !missing .isEmpty () && ((Range )missing .firstElement ()).end == 0 )
6156 {
62- Range firstRange = (Range )missing .firstElement ();
63- if (firstRange .getEnd () == 0 )
64- {
65- missing .removeElementAt (0 );
66- }
57+ missing .removeElementAt (0 );
6758 }
6859 return ;
6960 }
7061
71- for (int i = firstCandidate (fragment_offset ); i < missing .size (); ++i )
62+ for (int i = findStartIndex (fragment_offset ); i < missing .size (); ++i )
7263 {
7364 Range range = (Range )missing .elementAt (i );
74- if (range .getStart () >= fragment_end )
65+ if (range .start >= fragment_end )
7566 {
7667 break ;
7768 }
78- if (range .getEnd () > fragment_offset )
69+ if (range .end <= fragment_offset )
7970 {
71+ continue ;
72+ }
8073
81- int copyStart = Math .max (range .getStart () , fragment_offset );
82- int copyEnd = Math .min (range .getEnd () , fragment_end );
83- int copyLength = copyEnd - copyStart ;
74+ int copyStart = Math .max (range .start , fragment_offset );
75+ int copyEnd = Math .min (range .end , fragment_end );
76+ int copyLength = copyEnd - copyStart ;
8477
85- if (copyStart != range .getStart () && copyEnd != range .getEnd ()
86- && missing .size () >= maxMissingRanges )
78+ if (copyStart == range .start )
79+ {
80+ if (copyEnd == range .end )
8781 {
88- // splitting this range would pass the cap, so ignore the fragment for it
89- continue ;
82+ // TODO[tls] It should be possible to handle all removals together at the end (linearly)
83+ missing . removeElementAt ( i --) ;
9084 }
91-
92- System .arraycopy (buf , off + copyStart - fragment_offset , body , copyStart ,
93- copyLength );
94-
95- if (copyStart == range .getStart ())
85+ else
9686 {
97- if (copyEnd == range .getEnd ())
98- {
99- missing .removeElementAt (i --);
100- }
101- else
102- {
103- range .setStart (copyEnd );
104- }
87+ range .start = copyEnd ;
10588 }
106- else
89+ }
90+ else
91+ {
92+ if (copyEnd != range .end )
10793 {
108- if (copyEnd != range .getEnd ())
94+ // Splitting this range would exceed the limit, so ignore the fragment
95+ if (missing .size () >= maxMissingRanges )
10996 {
110- missing . insertElementAt ( new Range ( copyEnd , range . getEnd ()), ++ i ) ;
97+ continue ;
11198 }
112- range .setEnd (copyStart );
99+
100+ missing .insertElementAt (new Range (copyEnd , range .end ), ++i );
113101 }
102+ range .end = copyStart ;
114103 }
104+
105+ System .arraycopy (buf , off + copyStart - fragment_offset , body , copyStart , copyLength );
115106 }
116107 }
117108
118109 /**
119- * Index of the first range that can overlap a fragment starting at fragment_offset. The ranges
120- * are sorted and disjoint, so every earlier one ends at or below fragment_offset and could only
121- * be skipped over; searching for the start rather than rescanning from zero is what keeps the
122- * cost of a fragment independent of how many gaps precede it.
110+ * Find the index of the first range that might overlap a fragment starting at fragment_offset. The ranges are
111+ * sorted and disjoint, so every earlier one ends at or below fragment_offset and could only be skipped over.
123112 */
124- private int firstCandidate (int fragment_offset )
113+ private int findStartIndex (int fragment_offset )
125114 {
126115 int lo = 0 , hi = missing .size ();
127116 while (lo < hi )
128117 {
129118 int mid = (lo + hi ) >>> 1 ;
130- if (((Range )missing .elementAt (mid )).getEnd () > fragment_offset )
119+ if (((Range )missing .elementAt (mid )).end > fragment_offset )
131120 {
132121 hi = mid ;
133122 }
@@ -147,32 +136,12 @@ void reset()
147136
148137 private static class Range
149138 {
150- private int start , end ;
139+ int start , end ;
151140
152141 Range (int start , int end )
153142 {
154143 this .start = start ;
155144 this .end = end ;
156145 }
157-
158- int getStart ()
159- {
160- return start ;
161- }
162-
163- void setStart (int start )
164- {
165- this .start = start ;
166- }
167-
168- int getEnd ()
169- {
170- return end ;
171- }
172-
173- void setEnd (int end )
174- {
175- this .end = end ;
176- }
177146 }
178147}
0 commit comments