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