/* |
|
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package sun.security.jgss; |
|
import org.ietf.jgss.MessageProp; |
|
import java.util.LinkedList; |
|
/** |
|
* A utility class that implements a number list that keeps track of which |
|
* tokens have arrived by storing their token numbers in the list. It helps |
|
* detect old tokens, out of sequence tokens, and duplicate tokens. |
|
* |
|
* Each element of the list is an interval [a, b]. Its existence in the |
|
* list implies that all token numbers in the range a, a+1, ..., b-1, b |
|
* have arrived. Gaps in arrived token numbers are represented by the |
|
* numbers that fall in between two elements of the list. eg. {[a,b], |
|
* [c,d]} indicates that the token numbers b+1, ..., c-1 have not arrived |
|
* yet. |
|
* |
|
* The maximum number of intervals that we keep track of is |
|
* MAX_INTERVALS. Thus if there are too many gaps, then some of the older |
|
* sequence numbers are deleted from the list. The earliest sequence number |
|
* that exists in the list is the windowStart. The next expected sequence |
|
* number, or expectedNumber, is one greater than the latest sequence |
|
* number in the list. |
|
* |
|
* The list keeps track the first token number that should have arrived |
|
* (initNumber) so that it is able to detect if certain numbers occur after |
|
* the first valid token number but before windowStart. That would happen |
|
* if the number of elements (intervals) exceeds MAX_INTERVALS and some |
|
* initial elements had to be deleted. |
|
* |
|
* The working of the list is optimized for the normal case where the |
|
* tokens arrive in sequence. |
|
* |
|
* @author Mayank Upadhyay |
|
* @since 1.4 |
|
*/ |
|
public class TokenTracker { |
|
static final int MAX_INTERVALS = 5; |
|
private int initNumber; |
|
private int windowStart; |
|
private int expectedNumber; |
|
private int windowStartIndex = 0; |
|
private LinkedList<Entry> list = new LinkedList<Entry>(); |
|
public TokenTracker(int initNumber) { |
|
this.initNumber = initNumber; |
|
this.windowStart = initNumber; |
|
this.expectedNumber = initNumber; |
|
// Make an entry with one less than the expected first token |
|
Entry entry = new Entry(initNumber-1); |
|
list.add(entry); |
|
} |
|
/** |
|
* Returns the index for the entry into which this number will fit. If |
|
* there is none, it returns the index of the last interval |
|
* which precedes this number. It returns -1 if the number needs to be |
|
* a in a new interval ahead of the whole list. |
|
*/ |
|
private int getIntervalIndex(int number) { |
|
Entry entry = null; |
|
int i; |
|
// Start from the rear to optimize for the normal case |
|
for (i = list.size() - 1; i >= 0; i--) { |
|
entry = list.get(i); |
|
if (entry.compareTo(number) <= 0) |
|
break; |
|
} |
|
return i; |
|
} |
|
/** |
|
* Sets the sequencing and replay information for the given token |
|
* number. |
|
* |
|
* The following represents the number line with positions of |
|
* initNumber, windowStart, expectedNumber marked on it. Regions in |
|
* between them show the different sequencing and replay state |
|
* possibilites for tokens that fall in there. |
|
* |
|
* (1) windowStart |
|
* initNumber expectedNumber |
|
* | | |
|
* ---|---------------------------|--- |
|
* GAP | DUP/UNSEQ | GAP |
|
* |
|
* |
|
* (2) initNumber windowStart expectedNumber |
|
* | | | |
|
* ---|---------------|--------------|--- |
|
* GAP | OLD | DUP/UNSEQ | GAP |
|
* |
|
* |
|
* (3) windowStart |
|
* expectedNumber initNumber |
|
* | | |
|
* ---|---------------------------|--- |
|
* DUP/UNSEQ | GAP | DUP/UNSEQ |
|
* |
|
* |
|
* (4) expectedNumber initNumber windowStart |
|
* | | | |
|
* ---|---------------|--------------|--- |
|
* DUP/UNSEQ | GAP | OLD | DUP/UNSEQ |
|
* |
|
* |
|
* |
|
* (5) windowStart expectedNumber initNumber |
|
* | | | |
|
* ---|---------------|--------------|--- |
|
* OLD | DUP/UNSEQ | GAP | OLD |
|
* |
|
* |
|
* |
|
* (This analysis leaves out the possibility that expectedNumber passes |
|
* initNumber after wrapping around. That may be added later.) |
|
*/ |
|
synchronized public final void getProps(int number, MessageProp prop) { |
|
boolean gap = false; |
|
boolean old = false; |
|
boolean unsequenced = false; |
|
boolean duplicate = false; |
|
// System.out.println("\n\n=========="); |
|
// System.out.println("TokenTracker.getProps(): number=" + number); |
|
// System.out.println(toString()); |
|
int pos = getIntervalIndex(number); |
|
Entry entry = null; |
|
if (pos != -1) |
|
entry = list.get(pos); |
|
// Optimize for the expected case: |
|
if (number == expectedNumber) { |
|
expectedNumber++; |
|
} else { |
|
// Next trivial case is to check for duplicate |
|
if (entry != null && entry.contains(number)) |
|
duplicate = true; |
|
else { |
|
if (expectedNumber >= initNumber) { |
|
// Cases (1) and (2) |
|
if (number > expectedNumber) { |
|
gap = true; |
|
} else if (number >= windowStart) { |
|
unsequenced = true; |
|
} else if (number >= initNumber) { |
|
old = true; |
|
} else { |
|
gap = true; |
|
} |
|
} else { |
|
// Cases (3), (4) and (5) |
|
if (number > expectedNumber) { |
|
if (number < initNumber) { |
|
gap = true; |
|
} else if (windowStart >= initNumber) { |
|
if (number >= windowStart) { |
|
unsequenced = true; |
|
} else |
|
old = true; |
|
} else { |
|
old = true; |
|
} |
|
} else if (windowStart > expectedNumber) { |
|
unsequenced = true; |
|
} else if (number < windowStart) { |
|
old = true; |
|
} else |
|
unsequenced = true; |
|
} |
|
} |
|
} |
|
if (!duplicate && !old) |
|
add(number, pos); |
|
if (gap) |
|
expectedNumber = number+1; |
|
prop.setSupplementaryStates(duplicate, old, unsequenced, gap, |
|
0, null); |
|
// System.out.println("Leaving with state:"); |
|
// System.out.println(toString()); |
|
// System.out.println("==========\n"); |
|
} |
|
/** |
|
* Adds the number to the list just after the entry that is currently |
|
* at position prevEntryPos. If prevEntryPos is -1, then the number |
|
* will begin a new interval at the front of the list. |
|
*/ |
|
private void add(int number, int prevEntryPos) { |
|
Entry entry; |
|
Entry entryBefore = null; |
|
Entry entryAfter = null; |
|
boolean appended = false; |
|
boolean prepended = false; |
|
if (prevEntryPos != -1) { |
|
entryBefore = list.get(prevEntryPos); |
|
// Can this number simply be added to the previous interval? |
|
if (number == (entryBefore.getEnd() + 1)) { |
|
entryBefore.setEnd(number); |
|
appended = true; |
|
} |
|
} |
|
// Now check the interval that follows this number |
|
int nextEntryPos = prevEntryPos + 1; |
|
if ((nextEntryPos) < list.size()) { |
|
entryAfter = list.get(nextEntryPos); |
|
// Can this number simply be added to the next interval? |
|
if (number == (entryAfter.getStart() - 1)) { |
|
if (!appended) { |
|
entryAfter.setStart(number); |
|
} else { |
|
// Merge the two entries |
|
entryAfter.setStart(entryBefore.getStart()); |
|
list.remove(prevEntryPos); |
|
// Index of any entry following this gets decremented |
|
if (windowStartIndex > prevEntryPos) |
|
windowStartIndex--; |
|
} |
|
prepended = true; |
|
} |
|
} |
|
if (prepended || appended) |
|
return; |
|
/* |
|
* At this point we know that the number will start a new interval |
|
* which needs to be added to the list. We might have to recyle an |
|
* older entry in the list. |
|
*/ |
|
if (list.size() < MAX_INTERVALS) { |
|
entry = new Entry(number); |
|
if (prevEntryPos < windowStartIndex) |
|
windowStartIndex++; // due to the insertion which will happen |
|
} else { |
|
/* |
|
* Delete the entry that marks the start of the current window. |
|
* The marker will automatically point to the next entry in the |
|
* list when this happens. If the current entry is at the end |
|
* of the list then set the marker to the start of the list. |
|
*/ |
|
int oldWindowStartIndex = windowStartIndex; |
|
if (windowStartIndex == (list.size() - 1)) |
|
windowStartIndex = 0; |
|
entry = list.remove(oldWindowStartIndex); |
|
windowStart = list.get(windowStartIndex).getStart(); |
|
entry.setStart(number); |
|
entry.setEnd(number); |
|
if (prevEntryPos >= oldWindowStartIndex) { |
|
prevEntryPos--; // due to the deletion that just happened |
|
} else { |
|
/* |
|
* If the start of the current window just moved from the |
|
* end of the list to the front of the list, and if the new |
|
* entry will be added to the front of the list, then |
|
* the new entry is the actual window start. |
|
* eg, Consider { [-10, -8], ..., [-6, -3], [3, 9]}. In |
|
* this list, suppose the element [3, 9] is the start of |
|
* the window and has to be deleted to make place to add |
|
* [-12, -12]. The resultant list will be |
|
* {[-12, -12], [-10, -8], ..., [-6, -3]} and the new start |
|
* of the window should be the element [-12, -12], not |
|
* [-10, -8] which succeeded [3, 9] in the old list. |
|
*/ |
|
if (oldWindowStartIndex != windowStartIndex) { |
|
// windowStartIndex is 0 at this point |
|
if (prevEntryPos == -1) |
|
// The new entry is going to the front |
|
windowStart = number; |
|
} else { |
|
// due to the insertion which will happen: |
|
windowStartIndex++; |
|
} |
|
} |
|
} |
|
// Finally we are ready to actually add to the list at index |
|
// 'prevEntryPos+1' |
|
list.add(prevEntryPos+1, entry); |
|
} |
|
public String toString() { |
|
StringBuffer buf = new StringBuffer("TokenTracker: "); |
|
buf.append(" initNumber=").append(initNumber); |
|
buf.append(" windowStart=").append(windowStart); |
|
buf.append(" expectedNumber=").append(expectedNumber); |
|
buf.append(" windowStartIndex=").append(windowStartIndex); |
|
buf.append("\n\tIntervals are: {"); |
|
for (int i = 0; i < list.size(); i++) { |
|
if (i != 0) |
|
buf.append(", "); |
|
buf.append(list.get(i).toString()); |
|
} |
|
buf.append('}'); |
|
return buf.toString(); |
|
} |
|
/** |
|
* An entry in the list that represents the sequence of received |
|
* tokens. Each entry is actaully an interval of numbers, all of which |
|
* have been received. |
|
*/ |
|
class Entry { |
|
private int start; |
|
private int end; |
|
Entry(int number) { |
|
start = number; |
|
end = number; |
|
} |
|
/** |
|
* Returns -1 if this interval represented by this entry precedes |
|
* the number, 0 if the the number is contained in the interval, |
|
* and -1 if the interval occurs after the number. |
|
*/ |
|
final int compareTo(int number) { |
|
if (start > number) |
|
return 1; |
|
else if (end < number) |
|
return -1; |
|
else |
|
return 0; |
|
} |
|
final boolean contains(int number) { |
|
return (number >= start && |
|
number <= end); |
|
} |
|
final void append(int number) { |
|
if (number == (end + 1)) |
|
end = number; |
|
} |
|
final void setInterval(int start, int end) { |
|
this.start = start; |
|
this.end = end; |
|
} |
|
final void setEnd(int end) { |
|
this.end = end; |
|
} |
|
final void setStart(int start) { |
|
this.start = start; |
|
} |
|
final int getStart() { |
|
return start; |
|
} |
|
final int getEnd() { |
|
return end; |
|
} |
|
public String toString() { |
|
return ("[" + start + ", " + end + "]"); |
|
} |
|
} |
|
} |