001: package org.mortbay.jetty.security;
002:
003: import java.io.IOException;
004: import java.nio.ByteBuffer;
005: import java.nio.channels.SelectionKey;
006: import java.nio.channels.SocketChannel;
007:
008: import javax.net.ssl.SSLEngine;
009: import javax.net.ssl.SSLEngineResult;
010: import javax.net.ssl.SSLException;
011: import javax.net.ssl.SSLSession;
012:
013: import org.mortbay.io.Buffer;
014: import org.mortbay.io.nio.NIOBuffer;
015: import org.mortbay.io.nio.SelectorManager;
016: import org.mortbay.jetty.nio.SelectChannelConnector;
017: import org.mortbay.log.Log;
018:
019: /* ------------------------------------------------------------ */
020: /**
021: * SslHttpChannelEndPoint.
022: *
023: * @author Nik Gonzalez <ngonzalez@exist.com>
024: * @author Greg Wilkins <gregw@mortbay.com>
025: */
026: public class SslHttpChannelEndPoint extends
027: SelectChannelConnector.ConnectorEndPoint implements Runnable {
028: private static ByteBuffer[] __NO_BUFFERS = {};
029: private static ByteBuffer __EMPTY = ByteBuffer.allocate(0);
030: private static SSLEngineResult _result;
031:
032: private final SSLEngine _engine;
033: private final ByteBuffer _inBuffer;
034: private final NIOBuffer _inNIOBuffer;
035: private final ByteBuffer _outBuffer;
036: private final NIOBuffer _outNIOBuffer;
037:
038: private ByteBuffer _reuseBuffer;
039: private final ByteBuffer[] _outBuffers = new ByteBuffer[3];
040:
041: // ssl
042: private final SSLSession _session;
043:
044: /* ------------------------------------------------------------ */
045: public SslHttpChannelEndPoint(SocketChannel channel,
046: SelectorManager.SelectSet selectSet, SelectionKey key,
047: SSLEngine engine) throws SSLException, IOException {
048: super (channel, selectSet, key);
049:
050: // ssl
051: _engine = engine;
052: _engine.setUseClientMode(false);
053: _session = engine.getSession();
054:
055: _outNIOBuffer = new NIOBuffer(_session.getPacketBufferSize(),
056: true);
057: _outBuffer = _outNIOBuffer.getByteBuffer();
058: _inNIOBuffer = new NIOBuffer(_session.getPacketBufferSize(),
059: true);
060: _inBuffer = _inNIOBuffer.getByteBuffer();
061:
062: }
063:
064: /* ------------------------------------------------------------ */
065: public void close() throws IOException {
066: _engine.closeOutbound();
067:
068: try {
069: loop: while (_inBuffer.remaining() > 0) {
070: switch (_engine.getHandshakeStatus()) {
071: case FINISHED:
072: case NOT_HANDSHAKING:
073: break loop;
074:
075: case NEED_UNWRAP:
076: if (!fill(__EMPTY))
077: break loop; // TODO may need to reschedule?
078: break;
079:
080: case NEED_TASK: {
081: Runnable task;
082: while ((task = _engine.getDelegatedTask()) != null) {
083: task.run();
084: }
085: break;
086: }
087:
088: case NEED_WRAP: {
089: flush();
090:
091: SSLEngineResult result = null;
092: try {
093: _outBuffer.position(_outNIOBuffer.putIndex());
094: result = _engine.wrap(__NO_BUFFERS, _outBuffer);
095: } finally {
096: _outBuffer.position(0);
097: _outNIOBuffer.setGetIndex(0);
098: _outNIOBuffer.setPutIndex(result
099: .bytesProduced());
100: }
101:
102: flush();
103:
104: break;
105: }
106: }
107: }
108:
109: } finally {
110: super .close();
111: }
112:
113: }
114:
115: /* ------------------------------------------------------------ */
116: /*
117: */
118: public int fill(Buffer buffer) throws IOException {
119: ByteBuffer bbuf = extractInputBuffer(buffer);
120: int size = buffer.length();
121:
122: try {
123: fill(bbuf);
124:
125: loop: while (_inBuffer.remaining() > 0) {
126: switch (_engine.getHandshakeStatus()) {
127: case FINISHED:
128: case NOT_HANDSHAKING:
129: break loop;
130:
131: case NEED_UNWRAP:
132: if (!fill(bbuf))
133: break loop;
134: break;
135:
136: case NEED_TASK: {
137: Runnable task;
138: while ((task = _engine.getDelegatedTask()) != null) {
139: task.run();
140: }
141: break;
142: }
143:
144: case NEED_WRAP: {
145: flush();
146:
147: SSLEngineResult result = null;
148: try {
149: _outBuffer.position(_outNIOBuffer.putIndex());
150: result = _engine.wrap(__NO_BUFFERS, _outBuffer);
151: } finally {
152: _outBuffer.position(0);
153: _outNIOBuffer.setGetIndex(0);
154: _outNIOBuffer.setPutIndex(result
155: .bytesProduced());
156: }
157:
158: flush();
159:
160: break;
161: }
162: }
163: }
164:
165: } finally {
166: buffer.setPutIndex(bbuf.position());
167: bbuf.position(0);
168: }
169:
170: return buffer.length() - size;
171: }
172:
173: /* ------------------------------------------------------------ */
174: public int flush(Buffer buffer) throws IOException {
175: return flush(buffer, null, null);
176: }
177:
178: /* ------------------------------------------------------------ */
179: /*
180: */
181: public int flush(Buffer header, Buffer buffer, Buffer trailer)
182: throws IOException {
183: if (_outNIOBuffer.length() > 0) {
184: flush();
185: if (_outNIOBuffer.length() > 0)
186: return 0;
187: }
188:
189: _outBuffers[0] = extractOutputBuffer(header);
190: _outBuffers[1] = extractOutputBuffer(buffer);
191: _outBuffers[2] = extractOutputBuffer(trailer);
192:
193: try {
194: _outNIOBuffer.clear();
195: _outBuffer.position(0);
196: _outBuffer.limit(_outBuffer.capacity());
197: _result = _engine.wrap(_outBuffers, _outBuffer);
198: } finally {
199: _outBuffer.position(0);
200: _outNIOBuffer.setGetIndex(0);
201: _outNIOBuffer.setPutIndex(_result.bytesProduced());
202:
203: int consumed = _result.bytesConsumed();
204: if (consumed > 0 && header != null) {
205: int len = consumed < header.length() ? consumed
206: : header.length();
207: header.skip(len);
208: consumed -= len;
209: _outBuffers[0].position(0);
210: _outBuffers[0].limit(_outBuffers[0].capacity());
211: }
212: if (consumed > 0 && buffer != null) {
213: int len = consumed < buffer.length() ? consumed
214: : buffer.length();
215: buffer.skip(len);
216: consumed -= len;
217: _outBuffers[1].position(0);
218: _outBuffers[1].limit(_outBuffers[1].capacity());
219: }
220: if (consumed > 0 && trailer != null) {
221: int len = consumed < trailer.length() ? consumed
222: : trailer.length();
223: trailer.skip(len);
224: consumed -= len;
225: _outBuffers[1].position(0);
226: _outBuffers[1].limit(_outBuffers[1].capacity());
227: }
228: assert consumed == 0;
229: }
230:
231: flush();
232:
233: return _result.bytesConsumed();
234: }
235:
236: /* ------------------------------------------------------------ */
237: public void flush() throws IOException {
238: while (_outNIOBuffer.length() > 0) {
239: int flushed = super .flush(_outNIOBuffer);
240: if (flushed == 0) {
241: Thread.yield();
242: flushed = super .flush(_outNIOBuffer);
243: if (flushed == 0)
244: return;
245: }
246: }
247: }
248:
249: /* ------------------------------------------------------------ */
250: private ByteBuffer extractInputBuffer(Buffer buffer) {
251: assert buffer instanceof NIOBuffer;
252: NIOBuffer nbuf = (NIOBuffer) buffer;
253: ByteBuffer bbuf = nbuf.getByteBuffer();
254: bbuf.position(buffer.putIndex());
255: return bbuf;
256: }
257:
258: /* ------------------------------------------------------------ */
259: private ByteBuffer extractOutputBuffer(Buffer buffer) {
260: if (buffer == null)
261: return __EMPTY;
262:
263: ByteBuffer src = null;
264: NIOBuffer nBuf = null;
265:
266: if (buffer.buffer() instanceof NIOBuffer) {
267: nBuf = (NIOBuffer) buffer.buffer();
268: src = nBuf.getByteBuffer();
269: } else {
270: if (_reuseBuffer == null) {
271: _reuseBuffer = ByteBuffer.allocateDirect(_session
272: .getPacketBufferSize());
273: }
274: _reuseBuffer.put(buffer.asArray());
275: src = _reuseBuffer;
276: }
277:
278: if (src != null) {
279: src.position(buffer.getIndex());
280: src.limit(buffer.putIndex());
281: }
282:
283: return src;
284: }
285:
286: /* ------------------------------------------------------------ */
287: private boolean fill(ByteBuffer buffer) throws IOException {
288: int in_len = 0;
289:
290: if (!_inNIOBuffer.hasContent()
291: || (_result != null && _result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW)) {
292: _inNIOBuffer.clear();
293: while (_inNIOBuffer.space() > 0) {
294: int len = super .fill(_inNIOBuffer);
295: if (len <= 0)
296: break;
297: in_len += len;
298: }
299: }
300:
301: if (_inNIOBuffer.length() == 0)
302: return false;
303:
304: try {
305: _inBuffer.position(_inNIOBuffer.getIndex());
306: _inBuffer.limit(_inNIOBuffer.putIndex());
307: _result = _engine.unwrap(_inBuffer, buffer);
308: if (_result != null) {
309: if (_result.getStatus() == SSLEngineResult.Status.OK)
310: _inNIOBuffer.skip(_result.bytesConsumed());
311: else if (_result.getStatus() == SSLEngineResult.Status.CLOSED)
312: throw new IOException("sslEngine closed");
313: }
314:
315: } finally {
316: _inBuffer.position(0);
317: _inBuffer.limit(_inBuffer.capacity());
318: }
319:
320: switch (_result.getStatus()) {
321: case OK:
322: case CLOSED:
323: break;
324: case BUFFER_UNDERFLOW:
325: break;
326: case BUFFER_OVERFLOW:
327: buffer.clear();
328: break;
329: default:
330: Log.warn("unwrap " + _result);
331: throw new IOException(_result.toString());
332: }
333:
334: return (_result.bytesProduced() + _result.bytesConsumed()) > 0;
335: }
336:
337: /* ------------------------------------------------------------ */
338: /**
339: * Updates selection key. Adds operations types to the selection key as needed. No operations
340: * are removed as this is only done during dispatch. This method records the new key and
341: * schedules a call to syncKey to do the keyChange
342: */
343: protected void updateKey() {
344: synchronized (this ) {
345: int ops = _key == null ? 0 : _key.interestOps();
346: _interestOps = ops
347: | ((!_dispatched || _readBlocked) ? SelectionKey.OP_READ
348: : 0)
349: | (_writable && !_writeBlocked
350: && !isBufferingOutput() ? 0
351: : SelectionKey.OP_WRITE);
352: _writable = true; // Once writable is in ops, only removed with dispatch.
353:
354: if (_interestOps != ops) {
355: _selectSet.addChange(this );
356: _selectSet.wakeup();
357: }
358: }
359: }
360:
361: /* ------------------------------------------------------------ */
362: public boolean isBufferingInput() {
363: return _inNIOBuffer.hasContent();
364: }
365:
366: /* ------------------------------------------------------------ */
367: public boolean isBufferingOutput() {
368: return _outNIOBuffer.hasContent();
369: }
370:
371: /* ------------------------------------------------------------ */
372: public boolean isBufferred() {
373: return true;
374: }
375:
376: public SSLEngine getSSLEngine() {
377: return _engine;
378: }
379: }
|