blob: c98f1cc2c261848287634eb8d5c1d9e03cf4d380 [file] [log] [blame]
/*
* Copyright 1999-2007 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.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;
}
}