001: /*
002: * Copyright (c) xsocket.org, 2006 - 2008. All rights reserved.
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
019: * The latest copy of this software may be found on http://www.xsocket.org/
020: */
021: package org.xsocket.connection.spi;
022:
023: import java.io.IOException;
024: import java.nio.ByteBuffer;
025: import java.nio.channels.ClosedChannelException;
026: import java.util.ArrayList;
027: import java.util.IdentityHashMap;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.logging.Level;
031: import java.util.logging.Logger;
032:
033: import javax.net.ssl.SSLContext;
034:
035: /**
036: * activateable SSL io handler
037: *
038: * @author grro@xsocket.org
039: */
040: final class IoActivateableSSLHandler extends ChainableIoHandler
041: implements SSLProcessor.EventHandler {
042:
043: private static final Logger LOG = Logger
044: .getLogger(IoSSLHandler.class.getName());
045:
046: private enum Mode {
047: OFF, PLAIN_OUTGOING_SSL_INCOMING, PRE_SSL2, SSL
048: };
049:
050: private Mode mode = Mode.OFF;
051:
052: // receive & send queue
053: private IoQueue inNetDataQueue = new IoQueue();
054: private IoQueue outAppDataQueue = new IoQueue();
055:
056: // sync write support
057: private final PendingWriteMap pendingWriteMap = new PendingWriteMap();
058:
059: // event handling
060: private final IOEventHandler ioEventHandler = new IOEventHandler();
061:
062: // ssl stuff
063: private SSLProcessor sslProcessor = null;
064:
065: /**
066: * constructor
067: *
068: * @param successor the successor
069: * @param sslContext the ssl context to use
070: * @param isClientMode true, if is in client mode
071: * @param memoryManager the memory manager to use
072: * @throws IOException If some other I/O error occurs
073: */
074: IoActivateableSSLHandler(ChainableIoHandler successor,
075: SSLContext sslContext, boolean isClientMode,
076: IMemoryManager memoryManager) throws IOException {
077: super (successor);
078:
079: sslProcessor = new SSLProcessor(sslContext, isClientMode,
080: memoryManager, this );
081: }
082:
083: public void init(IIoHandlerCallback callbackHandler)
084: throws IOException {
085: setPreviousCallback(callbackHandler);
086: getSuccessor().init(ioEventHandler);
087: }
088:
089: /**
090: * {@inheritDoc}
091: */
092: public boolean reset() {
093: inNetDataQueue.drain();
094: outAppDataQueue.drain();
095:
096: pendingWriteMap.clear();
097:
098: return super .reset();
099: }
100:
101: /**
102: * {@inheritDoc}
103: */
104: public void setPreviousCallback(IIoHandlerCallback callbackHandler) {
105: super .setPreviousCallback(callbackHandler);
106: getSuccessor().setPreviousCallback(ioEventHandler);
107: }
108:
109: /**
110: * {@inheritDoc}
111: */
112: public final void close(boolean immediate) throws IOException {
113: if (!immediate) {
114: flushOutgoing();
115: }
116:
117: getSuccessor().close(immediate);
118: }
119:
120: public boolean isSSLActivated() {
121: return (mode == Mode.SSL);
122: }
123:
124: /**
125: * {@inheritDoc}
126: */
127: @Override
128: public int getPendingWriteDataSize() {
129: return outAppDataQueue.getSize()
130: + super .getPendingWriteDataSize();
131: }
132:
133: /**
134: * {@inheritDoc}
135: */
136: @Override
137: public boolean hasDataToSend() {
138: return (!outAppDataQueue.isEmpty() || super .hasDataToSend());
139: }
140:
141: /**
142: * {@inheritDoc}
143: */
144: public final void write(ByteBuffer[] buffers)
145: throws ClosedChannelException, IOException {
146: outAppDataQueue.append(buffers);
147: flushOutgoing();
148: }
149:
150: /**
151: * {@inheritDoc}
152: */
153: public void flushOutgoing() throws IOException {
154: if (mode == Mode.SSL) {
155: synchronized (sslProcessor) {
156: if (sslProcessor.isHandshaking()) {
157: sslProcessor.encrypt();
158:
159: } else {
160: if (!outAppDataQueue.isEmpty()) {
161: sslProcessor.encrypt(outAppDataQueue.drain());
162: }
163: }
164: }
165:
166: } else if ((mode == Mode.OFF)
167: || (mode == Mode.PLAIN_OUTGOING_SSL_INCOMING)) {
168: ByteBuffer[] data = outAppDataQueue.drain();
169: getSuccessor().write(data);
170: }
171: }
172:
173: /**
174: * set mode to plain write and encrypted read
175: *
176: * @return true, if mode has been set
177: */
178: public boolean preStartSecuredMode() {
179: if (mode == Mode.OFF) {
180: mode = Mode.PLAIN_OUTGOING_SSL_INCOMING;
181: return true;
182:
183: } else {
184: LOG.warning("connection is already in ssl mode (mode="
185: + mode + "). Ignore (pre)startSecured Mode");
186: return false;
187: }
188: }
189:
190: /**
191: * Return already received data to ssl handler (this data
192: * will be interpreted as encrypted data). <br>
193: *
194: * @param readQueue the queue with already received data
195: * @throws IOException if an io exception occurs
196: */
197: public void startSecuredMode(ByteBuffer[] data) throws IOException {
198: if (mode != Mode.PLAIN_OUTGOING_SSL_INCOMING) {
199: LOG
200: .warning("connection is not in non_receiving mode (mode="
201: + mode + ")");
202: return;
203: }
204:
205: mode = Mode.PRE_SSL2;
206:
207: if (data != null) {
208: if (data.length > 0) {
209: inNetDataQueue.addFirst(data);
210: }
211: }
212:
213: sslProcessor.start();
214: mode = Mode.SSL;
215:
216: flushOutgoing();
217:
218: readIncomingEncryptedData(inNetDataQueue.drain());
219: }
220:
221: public void onHandshakeFinished() throws IOException {
222: flushOutgoing();
223: }
224:
225: private synchronized void readIncomingEncryptedData(
226: ByteBuffer[] inNetDataList) throws ClosedChannelException,
227: IOException {
228: if (inNetDataList != null) {
229: if (LOG.isLoggable(Level.FINE)) {
230: int size = 0;
231: for (ByteBuffer buffer : inNetDataList) {
232: size += buffer.remaining();
233: }
234:
235: LOG.fine("received " + size + " bytes encrypted data");
236: }
237:
238: sslProcessor.decrypt(inNetDataList);
239: }
240: }
241:
242: public void onSSLProcessorClosed() throws IOException {
243: close(true);
244: }
245:
246: public void onDataDecrypted(ByteBuffer[] decryptedBufferList) {
247: if (decryptedBufferList == null) {
248: return;
249: }
250:
251: if (decryptedBufferList.length == 0) {
252: return;
253: }
254:
255: getPreviousCallback().onData(decryptedBufferList);
256: }
257:
258: public void onDataEncrypted(ByteBuffer plainData,
259: ByteBuffer encryptedData) throws IOException {
260: if (encryptedData.hasRemaining()) {
261: pendingWriteMap.add(plainData, encryptedData);
262: ByteBuffer[] buffers = new ByteBuffer[1];
263: buffers[0] = encryptedData;
264: getSuccessor().write(buffers);
265: }
266: }
267:
268: private final class IOEventHandler implements IIoHandlerCallback {
269:
270: public void onData(ByteBuffer[] data) {
271: try {
272: if (mode == Mode.OFF) {
273: getPreviousCallback().onData(data);
274:
275: } else if (mode == Mode.SSL) {
276: readIncomingEncryptedData(data);
277:
278: } else {
279: assert (mode == Mode.PLAIN_OUTGOING_SSL_INCOMING)
280: || (mode == Mode.PRE_SSL2);
281: inNetDataQueue.append(data);
282: return;
283: }
284: } catch (Exception e) {
285: if (LOG.isLoggable(Level.FINE)) {
286: LOG
287: .fine("["
288: + getId()
289: + "] error occured while receiving data. Reason: "
290: + e.toString());
291: }
292: }
293: }
294:
295: public void onConnect() {
296: getPreviousCallback().onConnect();
297: }
298:
299: public void onWriteException(IOException ioException,
300: ByteBuffer data) {
301: getPreviousCallback().onWriteException(ioException, data);
302: }
303:
304: public void onWritten(ByteBuffer data) {
305: ByteBuffer plainData = pendingWriteMap
306: .getPlainIfWritten(data);
307: if (plainData != null) {
308: getPreviousCallback().onWritten(plainData);
309: } else {
310: getPreviousCallback().onWritten(data);
311: }
312: }
313:
314: public void onDisconnect() {
315: //getSSLProcessor().destroy();
316: getPreviousCallback().onDisconnect();
317: }
318:
319: public void onConnectionAbnormalTerminated() {
320: getPreviousCallback().onConnectionAbnormalTerminated();
321: }
322:
323: public void onConnectionTimeout() {
324: getPreviousCallback().onConnectionTimeout();
325: }
326:
327: public void onIdleTimeout() {
328: getPreviousCallback().onIdleTimeout();
329: }
330: }
331:
332: private static final class PendingWriteMap {
333:
334: private Map<ByteBuffer, List<ByteBuffer>> plainEncryptedMapping = new IdentityHashMap<ByteBuffer, List<ByteBuffer>>();
335: private Map<ByteBuffer, ByteBuffer> encryptedPlainMapping = new IdentityHashMap<ByteBuffer, ByteBuffer>();
336:
337: public synchronized void add(ByteBuffer plain,
338: ByteBuffer encrypted) {
339: // ignore system data (plain is empty)
340: if (plain.limit() > 0) {
341: List<ByteBuffer> encryptedList = plainEncryptedMapping
342: .get(plain);
343: if (encryptedList == null) {
344: encryptedList = new ArrayList<ByteBuffer>();
345: plainEncryptedMapping.put(plain, encryptedList);
346: }
347:
348: encryptedList.add(encrypted);
349: encryptedPlainMapping.put(encrypted, plain);
350: }
351: }
352:
353: public synchronized ByteBuffer getPlainIfWritten(
354: ByteBuffer encrypted) {
355: ByteBuffer plain = encryptedPlainMapping.remove(encrypted);
356: if (plain != null) {
357: List<ByteBuffer> encryptedList = plainEncryptedMapping
358: .get(plain);
359: encryptedList.remove(encrypted);
360: if (encryptedList.isEmpty()) {
361: plainEncryptedMapping.remove(plain);
362: return plain;
363: }
364: }
365: return null;
366: }
367:
368: public synchronized void clear() {
369: plainEncryptedMapping.clear();
370: encryptedPlainMapping.clear();
371:
372: }
373: }
374: }
|