⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sslchannel.java

📁 Examples to create your Conferencing System in .NET, C# VOIP & Video Conferencing Systems using H.32
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
 * Copyright 2004 WIT-Software, Lda. 
 * - web: http://www.wit-software.com 
 * - email: info@wit-software.com
 *
 * All rights reserved. Relased under terms of the 
 * Creative Commons' Attribution-NonCommercial-ShareAlike license.
 */
package ssl;

import handlers.Channel;
import handlers.ChannelListener;
import io.SelectorThread;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;


/**
 * @author Nuno Santos
 */
public class SSLChannel extends Channel {
	private final static Logger log = Logger.getLogger("handlers");
//	private final static Logger log = new Logger();
	

	private final SSLSession session;
	private final SSLEngine engine;
	
	/** Application data decrypted from the data received from the peer.
	 * This buffer must have enough space for a full unwrap operation,
	 * so we can't use the buffer provided by the application, since we
	 * have no control over its size.
	 */
	private final ByteBuffer peerAppData;
	/** Network data received from the peer. Encrypted. */  
	private final ByteBuffer peerNetData;
	/** Network data to be sent to the peer. Encrypted. */
	private final ByteBuffer netData;
	
	/**
	 * Whether the listener is interested in read events
	 */
	private boolean appReadInterestSet = false;
	/**
	 * Whether the listener is interested in write events
	 */
	private boolean appWriteInterestSet = false;	
	
	
	private boolean channelReadInterestSet = false;
	private boolean channelWriteInterestSet = false;
	
	/**
	 * Set to true during the initial handshake. The initial handshake 
	 * is special since no application data can flow during it. Subsequent 
	 * handshake are dealt with in a somewhat different way. 
	 */
	private boolean initialHandshake = false;
	
	private SSLEngineResult.HandshakeStatus hsStatus;
	/** Used during handshake, for the operations that don't consume any data */
	private ByteBuffer dummy;
	
	private boolean shutdown = false;
	private boolean closed = false;
	/**
	 * Stores the result from the last operation performed by the SSLEngine 
	 */
	private SSLEngineResult.Status status = null;
	
	/**
	 * If an error occurs while processing a callback from the selector
	 * thread, the exception is saved in this field to be thrown to the
	 * application the next time it calls a public method of this class.
	 */
	private IOException asynchException = null;
	
	/**
	 * @param st
	 * @param sc
	 * @param listener
	 * @throws Exception
	 */
	public SSLChannel(
			SelectorThread st, 
			SocketChannel sc, 
			ChannelListener listener, 
			SSLEngine engine) throws Exception {
		super(st, sc, listener);		

		
		this.engine = engine;
		
		session = engine.getSession();
		peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
		peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());		
		netData = ByteBuffer.allocate(session.getPacketBufferSize());
		log.fine("peerNetData: " + peerNetData.capacity() + 
				", peerAppData: " + peerAppData.capacity() +
				", netData: " + netData.capacity());
		
		// The peerNetData buffer is assumed to be ready to be written,
		// while the other buffers are assumed to be ready to be read from.
		
		// Change the position of the buffers so that a 
		// call to hasRemaining() returns false. A buffer is considered
		// empty when the position is set to its limit, that is when
		// hasRemaining() returns false.		
		peerAppData.position(peerAppData.limit());
		netData.position(netData.limit());
		st.registerChannelNow(sc, 0, this);

		log.fine("Starting handshake");		
		engine.beginHandshake();
		hsStatus = engine.getHandshakeStatus();
		initialHandshake = true;
		dummy = ByteBuffer.allocate(0);
		doHandshake();
	}
	
	private void checkChannelStillValid() throws IOException {
		if (closed) {
			throw new ClosedChannelException();
		}
		if (asynchException != null) {
			IOException ioe = new IOException(
					"Asynchronous failure: " + asynchException.getMessage());
			ioe.initCause(asynchException);
			throw ioe;
		}
	}

	public int read(ByteBuffer dst) throws IOException {
//		log.fine("");		
		checkChannelStillValid();		
		if (initialHandshake) {
			return 0;
		}

		// Perhaps we should always try to read some data from
		// the socket. In some situations, it might not be possible
		// to unwrap some of the data stored on the buffers before
		// reading more.

		// Check if the stream is closed.
		if (engine.isInboundDone()) {
			// We reached EOF.
			return -1;
		}

		// First check if there is decrypted data waiting in the buffers
		if (!peerAppData.hasRemaining()) {
			int appBytesProduced = readAndUnwrap(); 
			if (appBytesProduced == -1 || appBytesProduced == 0) {
				return appBytesProduced;
			} 
		}
		
		// It's not certain that we will have some data decrypted ready to 
		// be sent to the application. Anyway, copy as much data as possible
		int limit = Math.min(peerAppData.remaining(), dst.remaining());
		for (int i = 0; i < limit; i++) {
			dst.put(peerAppData.get());
		}
		return limit;
	}
	
	private int readAndUnwrap() throws IOException {
		assert !peerAppData.hasRemaining() : "Application buffer not empty";
		// No decrypted data left on the buffers.
		// Try to read from the socket. There may be some data
		// on the peerNetData buffer, but it might not be sufficient.		
		int bytesRead = sc.read(peerNetData);
		log.fine("Read from socket: " + bytesRead);			
		if (bytesRead == -1) {
			// We will not receive any more data. Closing the engine
			// is a signal that the end of stream was reached.
			engine.closeInbound();			
			// EOF. But do we still have some useful data available? 
			if (peerNetData.position() == 0 ||
					status == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
				// Yup. Either the buffer is empty or it's in underflow,
				// meaning that there is not enough data to reassemble a
				// TLS packet. So we can return EOF.
				return -1;
			}
			// Although we reach EOF, we still have some data left to
			// be decrypted. We must process it	
		}

		// Prepare the application buffer to receive decrypted data
		peerAppData.clear();
		
		// Prepare the net data for reading. 
		peerNetData.flip();
		SSLEngineResult res;
		do {
			res = engine.unwrap(peerNetData, peerAppData);
			log.info("Unwrapping:\n" + res);
			// During an handshake renegotiation we might need to perform
			// several unwraps to consume the handshake data.
		} while (res.getStatus() == SSLEngineResult.Status.OK &&
				res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP &&
				res.bytesProduced() == 0);
		
		// If the initial handshake finish after an unwrap, we must activate
		// the application interestes, if any were set during the handshake
		if (res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) {
			finishInitialHandshake();
		}
		
		// If no data was produced, and the status is still ok, try to read once more
		if (peerAppData.position() == 0 && 
				res.getStatus() == SSLEngineResult.Status.OK &&
				peerNetData.hasRemaining()) {
			res = engine.unwrap(peerNetData, peerAppData);
			log.info("Unwrapping:\n" + res);			
		}

		/*
		 * The status may be:
		 * OK - Normal operation
		 * OVERFLOW - Should never happen since the application buffer is 
		 * 	sized to hold the maximum packet size.
		 * UNDERFLOW - Need to read more data from the socket. It's normal.
		 * CLOSED - The other peer closed the socket. Also normal.
		 */
		status = res.getStatus();
		hsStatus = res.getHandshakeStatus();
		// Should never happen, the peerAppData must always have enough space
		// for an unwrap operation
		assert status != SSLEngineResult.Status.BUFFER_OVERFLOW : 
			"Buffer should not overflow: " + res.toString();
	
		// The handshake status here can be different than NOT_HANDSHAKING
		// if the other peer closed the connection. So only check for it
		// after testing for closure.
		if (status == SSLEngineResult.Status.CLOSED) {
			log.fine("Connection is being closed by peer.");
			shutdown = true;
			doShutdown();
			return -1;
		}	
		
		// Prepare the buffer to be written again.
		peerNetData.compact();
		// And the app buffer to be read.
		peerAppData.flip();
		
		if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK ||
				hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP ||
				hsStatus == SSLEngineResult.HandshakeStatus.FINISHED) 
		{
			log.fine("Rehandshaking...");
			doHandshake();
		}
		
		return peerAppData.remaining();
	}


	/**
	 *
	 */
	public int write(ByteBuffer src) throws IOException {
		checkChannelStillValid();
		if (initialHandshake) {
			log.fine("Writing not possible during handshake");
			// Not ready to write
			return 0;
		}
		log.fine("Trying to write");
		
		// First, check if we still have some data waiting to be sent.
		if (netData.hasRemaining()) {
			// There is. Don't try to send it. We should be registered 
			// waiting for a write event from the selector thread			
			assert channelWriteInterestSet : "Write interest should be active" + netData;
			return 0;
		}
		assert !channelWriteInterestSet :	"Write interest should not be active";

		// There is no data left to be sent. Clear the buffer and get
		// ready to encrypt more data.
		netData.clear();
		SSLEngineResult res = engine.wrap(src, netData);
		log.info("Wrapping:\n" + res);
		// Prepare the buffer for reading
		netData.flip();
		flushData();

		// Return the number of bytes read 
		// from the source buffer
		return res.bytesConsumed();
	}

	/**
	 * This method may result in a read attempt from the socket.
	 */
	public void registerForRead() throws IOException {
		checkChannelStillValid();		
		if (!appReadInterestSet) {
			appReadInterestSet = true;
			if (initialHandshake) {
				// Wait for handshake to finish
				return;

			} else {				
				if (peerAppData.hasRemaining()) {
					// There is decrypted data available, so prepare 
					// to fire the read event to the application
					st.getSscManager().registerForRead(this);

				} else {
					// There is no decrypted data. But there may be some 
					// encrypted data.
					if (peerNetData.position() == 0 || 
							status == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
						// Must read more data since either there is no encrypted
						// data available or there is data available but not
						// enough to reassemble a packet.
						selectorRegisterForRead();						
					} else {
						// There is encrypted data available. It may or may not
						// be enough to reassemble a full packet. We have to check it.
						if (readAndUnwrap() == 0) {
							// Not possible to reassemble a full packet.
							selectorRegisterForRead();	
						} else {
							// either EOF or there is application data ready. In both
							// cases we must inform the application
							st.getSscManager().registerForRead(this);
						}
					}
				}
			}
		}
	}

	public void unregisterForRead() throws IOException {
		checkChannelStillValid();
		appReadInterestSet = false;
		st.getSscManager().unregisterForRead(this);
	}

	/**

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -