Path: blob/master/src/java.security.jgss/share/classes/sun/security/jgss/TokenTracker.java
41159 views
/*1* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.jgss;2627import org.ietf.jgss.MessageProp;28import java.util.LinkedList;2930/**31* A utility class that implements a number list that keeps track of which32* tokens have arrived by storing their token numbers in the list. It helps33* detect old tokens, out of sequence tokens, and duplicate tokens.34*35* Each element of the list is an interval [a, b]. Its existence in the36* list implies that all token numbers in the range a, a+1, ..., b-1, b37* have arrived. Gaps in arrived token numbers are represented by the38* numbers that fall in between two elements of the list. eg. {[a,b],39* [c,d]} indicates that the token numbers b+1, ..., c-1 have not arrived40* yet.41*42* The maximum number of intervals that we keep track of is43* MAX_INTERVALS. Thus if there are too many gaps, then some of the older44* sequence numbers are deleted from the list. The earliest sequence number45* that exists in the list is the windowStart. The next expected sequence46* number, or expectedNumber, is one greater than the latest sequence47* number in the list.48*49* The list keeps track the first token number that should have arrived50* (initNumber) so that it is able to detect if certain numbers occur after51* the first valid token number but before windowStart. That would happen52* if the number of elements (intervals) exceeds MAX_INTERVALS and some53* initial elements had to be deleted.54*55* The working of the list is optimized for the normal case where the56* tokens arrive in sequence.57*58* @author Mayank Upadhyay59* @since 1.460*/61public class TokenTracker {6263static final int MAX_INTERVALS = 5;6465private int initNumber;66private int windowStart;67private int expectedNumber;6869private int windowStartIndex = 0;7071private LinkedList<Entry> list = new LinkedList<Entry>();7273public TokenTracker(int initNumber) {7475this.initNumber = initNumber;76this.windowStart = initNumber;77this.expectedNumber = initNumber;7879// Make an entry with one less than the expected first token80Entry entry = new Entry(initNumber-1);8182list.add(entry);83}8485/**86* Returns the index for the entry into which this number will fit. If87* there is none, it returns the index of the last interval88* which precedes this number. It returns -1 if the number needs to be89* a in a new interval ahead of the whole list.90*/91private int getIntervalIndex(int number) {92Entry entry = null;93int i;94// Start from the rear to optimize for the normal case95for (i = list.size() - 1; i >= 0; i--) {96entry = list.get(i);97if (entry.compareTo(number) <= 0)98break;99}100return i;101}102103/**104* Sets the sequencing and replay information for the given token105* number.106*107* The following represents the number line with positions of108* initNumber, windowStart, expectedNumber marked on it. Regions in109* between them show the different sequencing and replay state110* possibilites for tokens that fall in there.111*112* (1) windowStart113* initNumber expectedNumber114* | |115* ---|---------------------------|---116* GAP | DUP/UNSEQ | GAP117*118*119* (2) initNumber windowStart expectedNumber120* | | |121* ---|---------------|--------------|---122* GAP | OLD | DUP/UNSEQ | GAP123*124*125* (3) windowStart126* expectedNumber initNumber127* | |128* ---|---------------------------|---129* DUP/UNSEQ | GAP | DUP/UNSEQ130*131*132* (4) expectedNumber initNumber windowStart133* | | |134* ---|---------------|--------------|---135* DUP/UNSEQ | GAP | OLD | DUP/UNSEQ136*137*138*139* (5) windowStart expectedNumber initNumber140* | | |141* ---|---------------|--------------|---142* OLD | DUP/UNSEQ | GAP | OLD143*144*145*146* (This analysis leaves out the possibility that expectedNumber passes147* initNumber after wrapping around. That may be added later.)148*/149synchronized public final void getProps(int number, MessageProp prop) {150151boolean gap = false;152boolean old = false;153boolean unsequenced = false;154boolean duplicate = false;155156// System.out.println("\n\n==========");157// System.out.println("TokenTracker.getProps(): number=" + number);158// System.out.println(toString());159160int pos = getIntervalIndex(number);161Entry entry = null;162if (pos != -1)163entry = list.get(pos);164165// Optimize for the expected case:166167if (number == expectedNumber) {168expectedNumber++;169} else {170171// Next trivial case is to check for duplicate172if (entry != null && entry.contains(number))173duplicate = true;174else {175176if (expectedNumber >= initNumber) {177178// Cases (1) and (2)179180if (number > expectedNumber) {181gap = true;182} else if (number >= windowStart) {183unsequenced = true;184} else if (number >= initNumber) {185old = true;186} else {187gap = true;188}189} else {190191// Cases (3), (4) and (5)192193if (number > expectedNumber) {194if (number < initNumber) {195gap = true;196} else if (windowStart >= initNumber) {197if (number >= windowStart) {198unsequenced = true;199} else200old = true;201} else {202old = true;203}204} else if (windowStart > expectedNumber) {205unsequenced = true;206} else if (number < windowStart) {207old = true;208} else209unsequenced = true;210}211}212}213214if (!duplicate && !old)215add(number, pos);216217if (gap)218expectedNumber = number+1;219220prop.setSupplementaryStates(duplicate, old, unsequenced, gap,2210, null);222223// System.out.println("Leaving with state:");224// System.out.println(toString());225// System.out.println("==========\n");226}227228/**229* Adds the number to the list just after the entry that is currently230* at position prevEntryPos. If prevEntryPos is -1, then the number231* will begin a new interval at the front of the list.232*/233private void add(int number, int prevEntryPos) {234235Entry entry;236Entry entryBefore = null;237Entry entryAfter = null;238239boolean appended = false;240boolean prepended = false;241242if (prevEntryPos != -1) {243entryBefore = list.get(prevEntryPos);244245// Can this number simply be added to the previous interval?246if (number == (entryBefore.getEnd() + 1)) {247entryBefore.setEnd(number);248appended = true;249}250}251252// Now check the interval that follows this number253254int nextEntryPos = prevEntryPos + 1;255if ((nextEntryPos) < list.size()) {256entryAfter = list.get(nextEntryPos);257258// Can this number simply be added to the next interval?259if (number == (entryAfter.getStart() - 1)) {260if (!appended) {261entryAfter.setStart(number);262} else {263// Merge the two entries264entryAfter.setStart(entryBefore.getStart());265list.remove(prevEntryPos);266// Index of any entry following this gets decremented267if (windowStartIndex > prevEntryPos)268windowStartIndex--;269}270prepended = true;271}272}273274if (prepended || appended)275return;276277/*278* At this point we know that the number will start a new interval279* which needs to be added to the list. We might have to recyle an280* older entry in the list.281*/282283if (list.size() < MAX_INTERVALS) {284entry = new Entry(number);285if (prevEntryPos < windowStartIndex)286windowStartIndex++; // due to the insertion which will happen287} else {288/*289* Delete the entry that marks the start of the current window.290* The marker will automatically point to the next entry in the291* list when this happens. If the current entry is at the end292* of the list then set the marker to the start of the list.293*/294int oldWindowStartIndex = windowStartIndex;295if (windowStartIndex == (list.size() - 1))296windowStartIndex = 0;297298entry = list.remove(oldWindowStartIndex);299windowStart = list.get(windowStartIndex).getStart();300entry.setStart(number);301entry.setEnd(number);302303if (prevEntryPos >= oldWindowStartIndex) {304prevEntryPos--; // due to the deletion that just happened305} else {306/*307* If the start of the current window just moved from the308* end of the list to the front of the list, and if the new309* entry will be added to the front of the list, then310* the new entry is the actual window start.311* eg, Consider { [-10, -8], ..., [-6, -3], [3, 9]}. In312* this list, suppose the element [3, 9] is the start of313* the window and has to be deleted to make place to add314* [-12, -12]. The resultant list will be315* {[-12, -12], [-10, -8], ..., [-6, -3]} and the new start316* of the window should be the element [-12, -12], not317* [-10, -8] which succeeded [3, 9] in the old list.318*/319if (oldWindowStartIndex != windowStartIndex) {320// windowStartIndex is 0 at this point321if (prevEntryPos == -1)322// The new entry is going to the front323windowStart = number;324} else {325// due to the insertion which will happen:326windowStartIndex++;327}328}329}330331// Finally we are ready to actually add to the list at index332// 'prevEntryPos+1'333334list.add(prevEntryPos+1, entry);335}336337public String toString() {338StringBuilder sb = new StringBuilder("TokenTracker: ");339sb.append(" initNumber=").append(initNumber);340sb.append(" windowStart=").append(windowStart);341sb.append(" expectedNumber=").append(expectedNumber);342sb.append(" windowStartIndex=").append(windowStartIndex);343sb.append("\n\tIntervals are: {");344for (int i = 0; i < list.size(); i++) {345if (i != 0)346sb.append(", ");347sb.append(list.get(i).toString());348}349sb.append('}');350return sb.toString();351}352353/**354* An entry in the list that represents the sequence of received355* tokens. Each entry is actaully an interval of numbers, all of which356* have been received.357*/358class Entry {359360private int start;361private int end;362363Entry(int number) {364start = number;365end = number;366}367368/**369* Returns -1 if this interval represented by this entry precedes370* the number, 0 if the number is contained in the interval,371* and -1 if the interval occurs after the number.372*/373final int compareTo(int number) {374if (start > number)375return 1;376else if (end < number)377return -1;378else379return 0;380}381382final boolean contains(int number) {383return (number >= start &&384number <= end);385}386387final void append(int number) {388if (number == (end + 1))389end = number;390}391392final void setInterval(int start, int end) {393this.start = start;394this.end = end;395}396397final void setEnd(int end) {398this.end = end;399}400401final void setStart(int start) {402this.start = start;403}404405final int getStart() {406return start;407}408409final int getEnd() {410return end;411}412413public String toString() {414return ("[" + start + ", " + end + "]");415}416417}418}419420421