Sending SSL close alert from both shutdownOutput() and close().

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@3217 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Simone Bordet 2011-05-19 21:28:41 +00:00
parent c6665c228a
commit 4ba3046a8b
1 changed files with 79 additions and 73 deletions

View File

@ -4,11 +4,11 @@
// All rights reserved. This program and the accompanying materials // All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0 // are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution. // and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at // The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html // http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at // The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php // http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses. // You may elect to redistribute this code under either of these licenses.
// ======================================================================== // ========================================================================
package org.eclipse.jetty.io.nio; package org.eclipse.jetty.io.nio;
@ -17,7 +17,6 @@ import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey; import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel; import java.nio.channels.SocketChannel;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLEngineResult.HandshakeStatus;
@ -43,11 +42,11 @@ import org.eclipse.jetty.util.log.Logger;
public class SslSelectChannelEndPoint extends SelectChannelEndPoint public class SslSelectChannelEndPoint extends SelectChannelEndPoint
{ {
static Logger __log = Log.getLogger("org.eclipse.jetty.http.ssl"); static Logger __log = Log.getLogger("org.eclipse.jetty.http.ssl");
private static final ByteBuffer[] __NO_BUFFERS={}; private static final ByteBuffer[] __NO_BUFFERS={};
private final Buffers _buffers; private final Buffers _buffers;
private final SSLEngine _engine; private final SSLEngine _engine;
private final SSLSession _session; private final SSLSession _session;
private volatile NIOBuffer _inNIOBuffer; private volatile NIOBuffer _inNIOBuffer;
@ -61,7 +60,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
private boolean _handshook=false; private boolean _handshook=false;
private boolean _allowRenegotiate=false; private boolean _allowRenegotiate=false;
private final boolean _debug = __log.isDebugEnabled(); // snapshot debug status for optimizer private final boolean _debug = __log.isDebugEnabled(); // snapshot debug status for optimizer
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine, int maxIdleTime) public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine, int maxIdleTime)
@ -69,30 +68,30 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
{ {
super(channel,selectSet,key, maxIdleTime); super(channel,selectSet,key, maxIdleTime);
_buffers=buffers; _buffers=buffers;
// ssl // ssl
_engine=engine; _engine=engine;
_session=engine.getSession(); _session=engine.getSession();
if (_debug) __log.debug(_session+" channel="+channel); if (_debug) __log.debug(_session+" channel="+channel);
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine) public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine)
throws IOException throws IOException
{ {
super(channel,selectSet,key); super(channel,selectSet,key);
_buffers=buffers; _buffers=buffers;
// ssl // ssl
_engine=engine; _engine=engine;
_session=engine.getSession(); _session=engine.getSession();
if (_debug) __log.debug(_session+" channel="+channel); if (_debug) __log.debug(_session+" channel="+channel);
} }
int _outCount; int _outCount;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private void needOutBuffer() private void needOutBuffer()
{ {
@ -103,14 +102,14 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
_outNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize()); _outNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
} }
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private void freeOutBuffer() private void freeOutBuffer()
{ {
synchronized (this) synchronized (this)
{ {
if (--_outCount<=0 && _outNIOBuffer!=null && _outNIOBuffer.length()==0) if (--_outCount<=0 && _outNIOBuffer!=null && _outNIOBuffer.length()==0)
{ {
_buffers.returnBuffer(_outNIOBuffer); _buffers.returnBuffer(_outNIOBuffer);
_outNIOBuffer=null; _outNIOBuffer=null;
_outCount=0; _outCount=0;
@ -129,12 +128,12 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
_inNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize()); _inNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
} }
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private void freeInBuffer() private void freeInBuffer()
{ {
synchronized (this) synchronized (this)
{ {
if (--_inCount<=0 &&_inNIOBuffer!=null && _inNIOBuffer.length()==0) if (--_inCount<=0 &&_inNIOBuffer!=null && _inNIOBuffer.length()==0)
{ {
_buffers.returnBuffer(_inNIOBuffer); _buffers.returnBuffer(_inNIOBuffer);
@ -157,7 +156,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
/** /**
* Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
* a vulnerability in SSL/TLS with re-negotiation. If your JVM * a vulnerability in SSL/TLS with re-negotiation. If your JVM
* does not have CVE-2009-3555 fixed, then re-negotiation should * does not have CVE-2009-3555 fixed, then re-negotiation should
* not be allowed. * not be allowed.
* @param allowRenegotiate true if re-negotiation is allowed (default false) * @param allowRenegotiate true if re-negotiation is allowed (default false)
*/ */
@ -198,15 +197,21 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@Override @Override
public void shutdownOutput() throws IOException public void shutdownOutput() throws IOException
{
sslClose();
super.shutdownOutput();
}
protected void sslClose() throws IOException
{ {
if (_closing) if (_closing)
return; return;
_closing=true; _closing=true;
// TODO - this really should not be done in a loop here - but with async callbacks. // TODO - this really should not be done in a loop here - but with async callbacks.
long end=System.currentTimeMillis()+getMaxIdleTime(); long end=System.currentTimeMillis()+getMaxIdleTime();
try try
{ {
while (isOpen() && isBufferingOutput()&& System.currentTimeMillis()<end) while (isOpen() && isBufferingOutput()&& System.currentTimeMillis()<end)
{ {
flush(); flush();
@ -220,7 +225,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
_engine.closeOutbound(); _engine.closeOutbound();
loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()) && System.currentTimeMillis()<end) loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()) && System.currentTimeMillis()<end)
{ {
while (isOpen() && isBufferingOutput() && System.currentTimeMillis()<end) while (isOpen() && isBufferingOutput() && System.currentTimeMillis()<end)
{ {
flush(); flush();
@ -235,7 +240,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
case NOT_HANDSHAKING: case NOT_HANDSHAKING:
_handshook=true; _handshook=true;
break loop; break loop;
case NEED_UNWRAP: case NEED_UNWRAP:
Buffer buffer =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize()); Buffer buffer =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
try try
@ -256,7 +261,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
_buffers.returnBuffer(buffer); _buffers.returnBuffer(buffer);
} }
break; break;
case NEED_TASK: case NEED_TASK:
{ {
Runnable task; Runnable task;
@ -266,13 +271,13 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
} }
break; break;
} }
case NEED_WRAP: case NEED_WRAP:
{ {
needOutBuffer(); needOutBuffer();
ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer(); ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
try try
{ {
if (_outNIOBuffer.length()>0) if (_outNIOBuffer.length()>0)
flush(); flush();
_outNIOBuffer.compact(); _outNIOBuffer.compact();
@ -293,7 +298,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
out_buffer.position(0); out_buffer.position(0);
freeOutBuffer(); freeOutBuffer();
} }
break; break;
} }
} }
@ -310,12 +315,13 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
@Override @Override
public void close() throws IOException public void close() throws IOException
{ {
sslClose();
super.close(); super.close();
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Fill the buffer with unencrypted bytes. /** Fill the buffer with unencrypted bytes.
* Called by a Http Parser when more data is * Called by a Http Parser when more data is
* needed to continue parsing a request or a response. * needed to continue parsing a request or a response.
*/ */
@Override @Override
@ -325,10 +331,10 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
// or indirect), so extract the NIO buffer that is wrapped // or indirect), so extract the NIO buffer that is wrapped
// by the passed jetty Buffer. // by the passed jetty Buffer.
final ByteBuffer bbuf=extractInputBuffer(buffer); final ByteBuffer bbuf=extractInputBuffer(buffer);
// remember the original size of the unencrypted buffer // remember the original size of the unencrypted buffer
int size=buffer.length(); int size=buffer.length();
HandshakeStatus initialStatus = _engine.getHandshakeStatus(); HandshakeStatus initialStatus = _engine.getHandshakeStatus();
//noinspection SynchronizationOnLocalVariableOrMethodParameter //noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (bbuf) synchronized (bbuf)
@ -341,7 +347,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
unwrap(bbuf); unwrap(bbuf);
// Loop through the SSL engine state machine // Loop through the SSL engine state machine
int wraps=0; int wraps=0;
loop: while (true) loop: while (true)
{ {
@ -349,11 +355,11 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
if (isBufferingOutput()) if (isBufferingOutput())
{ {
// we must flush it, as the other end might be // we must flush it, as the other end might be
// waiting for that outgoing data before sending // waiting for that outgoing data before sending
// more incoming data // more incoming data
flush(); flush();
// If we were unable to flush all the data, then // If we were unable to flush all the data, then
// we should break the loop and wait for the call // we should break the loop and wait for the call
// back to handle when the SelectSet detects that // back to handle when the SelectSet detects that
// the channel is writable again. // the channel is writable again.
@ -370,7 +376,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
// so return -1 to signal upwards // so return -1 to signal upwards
if (_closing) if (_closing)
return -1; return -1;
// otherwise we break loop with the data we have unwrapped. // otherwise we break loop with the data we have unwrapped.
break loop; break loop;
@ -390,15 +396,15 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
case NEED_TASK: case NEED_TASK:
{ {
// A task needs to be run, so run it! // A task needs to be run, so run it!
Runnable task; Runnable task;
while ((task=_engine.getDelegatedTask())!=null) while ((task=_engine.getDelegatedTask())!=null)
{ {
task.run(); task.run();
} }
// Detect SUN JVM Bug!!! // Detect SUN JVM Bug!!!
if(initialStatus==HandshakeStatus.NOT_HANDSHAKING && if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
_engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && wraps==0) _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && wraps==0)
{ {
// This should be NEED_WRAP // This should be NEED_WRAP
@ -439,7 +445,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
case CLOSED: case CLOSED:
_closing=true; _closing=true;
} }
_outNIOBuffer.setPutIndex(put+_result.bytesProduced()); _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
} }
catch(SSLException e) catch(SSLException e)
@ -456,7 +462,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
// flush the encrypted outNIOBuffer // flush the encrypted outNIOBuffer
flush(); flush();
freeOutBuffer(); freeOutBuffer();
break; break;
} }
} }
@ -470,10 +476,10 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
} }
// return the number of unencrypted bytes filled. // return the number of unencrypted bytes filled.
int filled=buffer.length()-size; int filled=buffer.length()-size;
if (filled>0) if (filled>0)
_handshook=true; _handshook=true;
return filled; return filled;
} }
} }
@ -486,20 +492,20 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/* /*
*/ */
@Override @Override
public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
{ {
int consumed=0; int consumed=0;
int available=header.length(); int available=header.length();
if (buffer!=null) if (buffer!=null)
available+=buffer.length(); available+=buffer.length();
needOutBuffer(); needOutBuffer();
ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer(); ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
loop: while (true) loop: while (true)
{ {
if (_outNIOBuffer.length()>0) if (_outNIOBuffer.length()>0)
{ {
flush(); flush();
@ -517,7 +523,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
consumed= -1; consumed= -1;
break loop; break loop;
} }
int c; int c;
if (header!=null && header.length()>0) if (header!=null && header.length()>0)
{ {
@ -526,10 +532,10 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
else else
c=wrap(header); c=wrap(header);
} }
else else
c=wrap(buffer); c=wrap(buffer);
if (c>0) if (c>0)
{ {
_handshook=true; _handshook=true;
@ -542,7 +548,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
consumed=-1; consumed=-1;
break loop; break loop;
} }
break; break;
case NEED_UNWRAP: case NEED_UNWRAP:
@ -560,7 +566,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
{ {
_buffers.returnBuffer(buf); _buffers.returnBuffer(buf);
} }
break; break;
case NEED_TASK: case NEED_TASK:
@ -619,14 +625,14 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
freeOutBuffer(); freeOutBuffer();
return consumed; return consumed;
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@Override @Override
public void flush() throws IOException public void flush() throws IOException
{ {
if (_outNIOBuffer==null) if (_outNIOBuffer==null)
return; return;
int len=_outNIOBuffer.length(); int len=_outNIOBuffer.length();
if (isBufferingOutput()) if (isBufferingOutput())
{ {
@ -645,7 +651,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
} }
} }
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private void checkRenegotiate() throws IOException private void checkRenegotiate() throws IOException
{ {
@ -653,7 +659,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
{ {
Log.warn("SSL renegotiate denied: "+_channel); Log.warn("SSL renegotiate denied: "+_channel);
super.close(); super.close();
} }
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -674,14 +680,14 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
{ {
needInBuffer(); needInBuffer();
ByteBuffer in_buffer=_inNIOBuffer.getByteBuffer(); ByteBuffer in_buffer=_inNIOBuffer.getByteBuffer();
if (_inNIOBuffer.hasContent()) if (_inNIOBuffer.hasContent())
_inNIOBuffer.compact(); _inNIOBuffer.compact();
else else
_inNIOBuffer.clear(); _inNIOBuffer.clear();
int total_filled=0; int total_filled=0;
// loop filling as much encrypted data as we can into the buffer // loop filling as much encrypted data as we can into the buffer
while (_inNIOBuffer.space()>0 && super.isOpen()) while (_inNIOBuffer.space()>0 && super.isOpen())
{ {
@ -709,7 +715,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
break; break;
} }
} }
// If we have no progress and no data // If we have no progress and no data
if (total_filled==0 && _inNIOBuffer.length()==0) if (total_filled==0 && _inNIOBuffer.length()==0)
{ {
@ -729,11 +735,11 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
in_buffer.position(_inNIOBuffer.getIndex()); in_buffer.position(_inNIOBuffer.getIndex());
in_buffer.limit(_inNIOBuffer.putIndex()); in_buffer.limit(_inNIOBuffer.putIndex());
_result=null; _result=null;
// Do the unwrap // Do the unwrap
_result=_engine.unwrap(in_buffer,buffer); _result=_engine.unwrap(in_buffer,buffer);
if (_debug) __log.debug(_session+" unwrap unwrap "+_result); if (_debug) __log.debug(_session+" unwrap unwrap "+_result);
// skip the bytes consumed // skip the bytes consumed
_inNIOBuffer.skip(_result.bytesConsumed()); _inNIOBuffer.skip(_result.bytesConsumed());
} }
@ -756,9 +762,9 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
{ {
case BUFFER_OVERFLOW: case BUFFER_OVERFLOW:
throw new IllegalStateException(_result.toString()+" "+buffer.position()+" "+buffer.limit()); throw new IllegalStateException(_result.toString()+" "+buffer.position()+" "+buffer.limit());
case BUFFER_UNDERFLOW: case BUFFER_UNDERFLOW:
// Not enough data, // Not enough data,
// If we are closed, we will never get more, so EOF // If we are closed, we will never get more, so EOF
// else return and we will be tried again // else return and we will be tried again
// later when more data arriving causes another dispatch. // later when more data arriving causes another dispatch.
@ -771,7 +777,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
throw new EofException(); throw new EofException();
} }
return (total_filled > 0); return (total_filled > 0);
case CLOSED: case CLOSED:
_closing=true; _closing=true;
// return true is some bytes somewhere were moved about. // return true is some bytes somewhere were moved about.
@ -785,13 +791,13 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
} }
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private ByteBuffer extractOutputBuffer(Buffer buffer) private ByteBuffer extractOutputBuffer(Buffer buffer)
{ {
if (buffer.buffer() instanceof NIOBuffer) if (buffer.buffer() instanceof NIOBuffer)
return ((NIOBuffer)buffer.buffer()).getByteBuffer(); return ((NIOBuffer)buffer.buffer()).getByteBuffer();
return ByteBuffer.wrap(buffer.array()); return ByteBuffer.wrap(buffer.array());
} }
@ -799,7 +805,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
private int wrap(final Buffer header, final Buffer buffer) throws IOException private int wrap(final Buffer header, final Buffer buffer) throws IOException
{ {
_gather[0]=extractOutputBuffer(header); _gather[0]=extractOutputBuffer(header);
synchronized(_gather[0]) synchronized(_gather[0])
{ {
_gather[0].position(header.getIndex()); _gather[0].position(header.getIndex());
@ -857,20 +863,20 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
_gather[1].limit(_gather[1].capacity()); _gather[1].limit(_gather[1].capacity());
} }
assert consumed==0; assert consumed==0;
freeOutBuffer(); freeOutBuffer();
} }
} }
} }
} }
switch(_result.getStatus()) switch(_result.getStatus())
{ {
case BUFFER_OVERFLOW: case BUFFER_OVERFLOW:
case BUFFER_UNDERFLOW: case BUFFER_UNDERFLOW:
Log.warn("unwrap {}",_result); Log.warn("unwrap {}",_result);
case OK: case OK:
return _result.bytesConsumed(); return _result.bytesConsumed();
case CLOSED: case CLOSED:
@ -890,7 +896,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
synchronized(_gather[0]) synchronized(_gather[0])
{ {
ByteBuffer bb; ByteBuffer bb;
_gather[0].position(buffer.getIndex()); _gather[0].position(buffer.getIndex());
_gather[0].limit(buffer.putIndex()); _gather[0].limit(buffer.putIndex());
@ -930,7 +936,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
_gather[0].limit(_gather[0].capacity()); _gather[0].limit(_gather[0].capacity());
} }
assert consumed==0; assert consumed==0;
freeOutBuffer(); freeOutBuffer();
} }
} }
@ -940,7 +946,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
case BUFFER_OVERFLOW: case BUFFER_OVERFLOW:
case BUFFER_UNDERFLOW: case BUFFER_UNDERFLOW:
Log.warn("unwrap {}",_result); Log.warn("unwrap {}",_result);
case OK: case OK:
return _result.bytesConsumed(); return _result.bytesConsumed();
case CLOSED: case CLOSED: