/*
 * Copyright (c) 2000, 2018, 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.
 */

/*
 *
 *  (C) Copyright IBM Corp. 1999 All Rights Reserved.
 *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
 */

package sun.security.krb5.internal.rcache;

import sun.security.krb5.internal.Krb5;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
import sun.security.krb5.internal.KerberosTime;
import sun.security.krb5.internal.KrbApErrException;

This class provides an efficient caching mechanism to store AuthTimeWithHash from client authenticators. The cache minimizes the memory usage by doing self-cleanup of expired items in the cache. AuthTimeWithHash objects inside a cache are always sorted from big (new) to small (old) as determined by {@see AuthTimeWithHash#compareTo}. In the most common case a newcomer should be newer than the first element.
Author:Yanni Zhang
/** * This class provides an efficient caching mechanism to store AuthTimeWithHash * from client authenticators. The cache minimizes the memory usage by doing * self-cleanup of expired items in the cache. * * AuthTimeWithHash objects inside a cache are always sorted from big (new) to * small (old) as determined by {@see AuthTimeWithHash#compareTo}. In the most * common case a newcomer should be newer than the first element. * * @author Yanni Zhang */
public class AuthList { private final LinkedList<AuthTimeWithHash> entries; private final int lifespan; // entries.getLast().ctime, updated after each cleanup. private volatile int oldestTime = Integer.MIN_VALUE;
Constructs a AuthList.
/** * Constructs a AuthList. */
public AuthList(int lifespan) { this.lifespan = lifespan; entries = new LinkedList<>(); }
Puts the authenticator timestamp into the cache in descending order, and throw an exception if it's already there.
/** * Puts the authenticator timestamp into the cache in descending order, * and throw an exception if it's already there. */
public synchronized void put(AuthTimeWithHash t, KerberosTime currentTime) throws KrbApErrException { if (entries.isEmpty()) { entries.addFirst(t); oldestTime = t.ctime; return; } else { AuthTimeWithHash temp = entries.getFirst(); int cmp = temp.compareTo(t); if (cmp < 0) { // This is the most common case, newly received authenticator // has larger timestamp. entries.addFirst(t); } else if (cmp == 0) { throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); } else { //unless client clock being re-adjusted. ListIterator<AuthTimeWithHash> it = entries.listIterator(1); boolean found = false; while (it.hasNext()) { temp = it.next(); cmp = temp.compareTo(t); if (cmp < 0) { // Find an older one, put in front of it entries.add(entries.indexOf(temp), t); found = true; break; } else if (cmp == 0) { throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); } } if (!found) { // All is newer than the newcomer. Sigh. entries.addLast(t); } } } // let us cleanup while we are here long timeLimit = currentTime.getSeconds() - lifespan; // Only trigger a cleanup when the earliest entry is // lifespan + 5 sec ago. This ensures a cleanup is done // at most every 5 seconds so that we don't always // addLast(removeLast). if (oldestTime > timeLimit - 5) { return; } // and we remove the *enough* old ones (1 lifetime ago) while (!entries.isEmpty()) { AuthTimeWithHash removed = entries.removeLast(); if (removed.ctime >= timeLimit) { entries.addLast(removed); oldestTime = removed.ctime; return; } } oldestTime = Integer.MIN_VALUE; } public boolean isEmpty() { return entries.isEmpty(); } public String toString() { StringBuilder sb = new StringBuilder(); Iterator<AuthTimeWithHash> iter = entries.descendingIterator(); int pos = entries.size(); while (iter.hasNext()) { AuthTimeWithHash at = iter.next(); sb.append('#').append(pos--).append(": ") .append(at.toString()).append('\n'); } return sb.toString(); } }