/* * Copyright (c) 1999, 2007, 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.ssl; import java.io.*; import java.net.*; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import java.util.NoSuchElementException; import java.util.Vector; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSessionContext; import javax.net.ssl.SSLSessionBindingListener; import javax.net.ssl.SSLSessionBindingEvent; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import sun.misc.Cache; final class SSLSessionContextImpl implements SSLSessionContext { private Cache sessionCache = new Cache(); private Cache sessionHostPortCache = new Cache(); private int cacheLimit; private long timeoutMillis; private static final Debug debug = Debug.getInstance("ssl"); // file private SSLSessionContextImpl() { cacheLimit = getCacheLimit(); timeoutMillis = 86400000; // default, 24 hours } /** * Returns the SSL session object associated with the * specific session ID passed. */ public SSLSession getSession(byte[] id) { SSLSession sess = (SSLSession) sessionCache.get( new SessionId(id)); return checkTimeValidity(sess); } /** * Returns an enumeration of the active SSL sessions. */ public Enumeration<byte[]> getIds() { Vector<byte[]> v = new Vector<byte[]>(sessionCache.size()); SessionId sessId; for (Enumeration e = sessionCache.keys(); e.hasMoreElements(); ) { sessId = (SessionId) e.nextElement(); if (!isTimedout((SSLSession)sessionCache.get(sessId))) v.addElement(sessId.getId()); } return v.elements(); } public void setSessionTimeout(int seconds) throws IllegalArgumentException { if (seconds < 0) throw new IllegalArgumentException(); timeoutMillis = seconds * 1000L; } public int getSessionTimeout() { return (int) (timeoutMillis / 1000); } public void setSessionCacheSize(int size) throws IllegalArgumentException { if (size < 0) throw new IllegalArgumentException(); cacheLimit = size; /** * If cache size limit is reduced, when the cache is full to its * previous limit, trim the cache before its contents * are used. */ if ((cacheLimit != 0) && (sessionCache.size() > cacheLimit)) adjustCacheSizeTo(cacheLimit); } public int getSessionCacheSize() { return cacheLimit; } SSLSessionImpl get(byte[] id) { return (SSLSessionImpl) getSession(id); } /** * Returns the SSL session object associated with the * specific host name and port number passed. */ SSLSessionImpl get(String hostname, int port) { /* * If no session caching info is available, we won't * get one, so exit before doing a lookup. */ if (hostname == null && port == -1) { return null; } SSLSession sess = (SSLSessionImpl) sessionHostPortCache .get(getKey(hostname, port)); return (SSLSessionImpl) checkTimeValidity(sess); } private String getKey(String hostname, int port) { return (hostname + ":" + String.valueOf(port)) .toLowerCase(); } void put(SSLSessionImpl s) { // make space for the new session to be added if ((cacheLimit != 0) && (sessionCache.size() >= cacheLimit)) adjustCacheSizeTo(cacheLimit - 1); /* * Can always add the session id. */ sessionCache.put(s.getSessionId(), s); /* * If no hostname/port info is available, don't add this one. */ if ((s.getPeerHost() != null) && (s.getPeerPort() != -1)) { sessionHostPortCache.put( getKey(s.getPeerHost(), s.getPeerPort()), s); } s.setContext(this); } private void adjustCacheSizeTo(int targetSize) { int cacheSize = sessionCache.size(); if (targetSize < 0) return; while (cacheSize > targetSize) { SSLSessionImpl lru = null; SSLSessionImpl s = null; Enumeration e; if (debug != null && Debug.isOn("sessioncache")) { System.out.println("exceeded cache limit of " + cacheLimit); } /* * Count the number of elements in the cache. The size() method * does not reflect the cache entries that are no longer available, * i.e entries that are garbage collected (the cache entries are * held using soft references and are garbage collected when not * in use). */ int count; for (count = 0, e = sessionCache.elements(); e.hasMoreElements(); count++) { try { s = (SSLSessionImpl)e.nextElement(); } catch (NoSuchElementException nsee) { break; } if (isTimedout(s)) { lru = s; break; } else if ((lru == null) || (s.getLastAccessedTime() < lru.getLastAccessedTime())) { lru = s; } } if ((lru != null) && (count > targetSize)) { if (debug != null && Debug.isOn("sessioncache")) { System.out.println("uncaching " + lru); } lru.invalidate(); count--; // element removed from the cache } cacheSize = count; } } // file private void remove(SessionId key) { SSLSessionImpl s = (SSLSessionImpl) sessionCache.get(key); sessionCache.remove(key); sessionHostPortCache.remove(getKey(s.getPeerHost(), s.getPeerPort())); } private int getCacheLimit() { int cacheLimit = 0; try { String s = java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<String>() { public String run() { return System.getProperty( "javax.net.ssl.sessionCacheSize"); } }); cacheLimit = (s != null) ? Integer.valueOf(s).intValue() : 0; } catch (Exception e) { } return (cacheLimit > 0) ? cacheLimit : 0; } SSLSession checkTimeValidity(SSLSession sess) { if (isTimedout(sess)) { sess.invalidate(); return null; } else return sess; } boolean isTimedout(SSLSession sess) { if (timeoutMillis == 0) return false; if ((sess != null) && ((sess.getCreationTime() + timeoutMillis) <= (System.currentTimeMillis()))) return true; return false; } }