package org.playorm.nio.impl.cm.secure; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.NotYetConnectedException; import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ssl.SSLEngine; import org.playorm.nio.api.channels.TCPChannel; import org.playorm.nio.api.deprecated.ConnectionCallback; import org.playorm.nio.api.handlers.DataListener; import org.playorm.nio.api.handlers.FutureOperation; import org.playorm.nio.api.handlers.OperationCallback; import org.playorm.nio.api.libs.ChannelSession; import org.playorm.nio.api.libs.SSLEngineFactory; import org.playorm.nio.impl.util.FutureOperationImpl; import org.playorm.nio.impl.util.UtilTCPChannel; import org.playorm.nio.impl.util.UtilWaitForConnect; class SecTCPChannel extends UtilTCPChannel implements TCPChannel { private static final Logger log = Logger.getLogger(SecTCPChannel.class.getName()); private boolean isConnecting = false; private SSLEngineFactory sslFactory; private SecReaderProxy reader; private SecSSLListener connectProxy; //Server Socket created by TCPServerSocket public SecTCPChannel(TCPChannel channel) { super(channel); connectProxy = new SecSSLListener(this); reader = new SecReaderProxy(connectProxy); } //Client Socket created by channel manager public SecTCPChannel(TCPChannel channel, SSLEngineFactory sslFactory) { this(channel); this.sslFactory = sslFactory; } public FutureOperation openSSL(SSLEngine engine) { TCPChannel realChannel = getRealChannel(); throw new UnsupportedOperationException("not supported just yet, but let us know and we will quickly add it"); } public FutureOperation closeSSL() { TCPChannel realChannel = getRealChannel(); throw new UnsupportedOperationException("not supported just yet, but let us know and we will quickly add it"); } @Override public FutureOperation connect(SocketAddress addr) { TCPChannel realChannel = getRealChannel(); if(sslFactory == null) throw new RuntimeException(realChannel+"This socket is already connected"); isConnecting = true; FutureOperationImpl newFuture = new FutureOperationImpl(); SecProxyConnectOpCb connectCb = new SecProxyConnectOpCb(this, sslFactory, newFuture); FutureOperation lowerFuture = realChannel.connect(addr); lowerFuture.setListener(connectCb); return newFuture; } public FutureOperation write(ByteBuffer b) { if(reader.getHandler() == null) throw new NotYetConnectedException(); FutureOperationImpl future = new FutureOperationImpl(); SecProxyWriteHandler holder = new SecProxyWriteHandler(this, future); reader.getHandler().feedPlainPacket(b, holder); return future; } public FutureOperation close() { reader.close(); TCPChannel realChannel = getRealChannel(); realChannel.oldClose(); FutureOperationImpl newFuture = new FutureOperationImpl(); FutureOperation future = realChannel.close(); future.setListener(new SecProxyWriteHandler(this, newFuture)); return newFuture; } public int oldWrite(ByteBuffer b) { if(reader.getHandler() == null) throw new NotYetConnectedException(); int remain = b.remaining(); reader.getHandler().feedPlainPacket(b, null); TCPChannel realChannel = getRealChannel(); if(b.hasRemaining()) throw new RuntimeException(realChannel+"Bug, not all data written, buf="+b); return remain; } public void oldWrite(ByteBuffer b, OperationCallback h) { if(reader.getHandler() == null) throw new NotYetConnectedException(); SecProxyWriteHandler holder = new SecProxyWriteHandler(this, h); reader.getHandler().feedPlainPacket(b, holder); } /** * Not thread safe compatible with connect. You should call this method * on the same right after connect */ public synchronized void registerForReads(DataListener listener) { //TODO: this is a big problem, if they don't register for a read before the connect, //we will not receive the certificate info!!!! TCPChannel realChannel = getRealChannel(); connectProxy.setClientHandler(listener); if(log.isLoggable(Level.FINEST)) log.finest(realChannel+" about to register for reads"); if(!isConnecting) { if(log.isLoggable(Level.FINEST)) log.finest(realChannel+" register for reads"); realChannel.registerForReads(reader); } } public synchronized void unregisterForReads() { TCPChannel realChannel = getRealChannel(); //this first call ensure that if we are connecting, the //real unregisterForReads happens once connected... if(log.isLoggable(Level.FINEST)) log.finest(realChannel+" about to unregister for reads"); if(!isConnecting) { if(log.isLoggable(Level.FINEST)) log.finest(realChannel+" unregister for reads"); realChannel.unregisterForReads(); } connectProxy.setClientHandler(null); } public synchronized void oldConnect(SocketAddress addr, ConnectionCallback c) { TCPChannel realChannel = getRealChannel(); if(c == null) throw new IllegalArgumentException(realChannel+"ConnectCallback cannot be null"); else if(sslFactory == null) throw new RuntimeException(realChannel+"This socket is already connected"); isConnecting = true; SecProxyConnectCb connectCb = new SecProxyConnectCb(this, sslFactory, c); realChannel.oldConnect(addr, connectCb); } public synchronized void resetRegisterForReadState() throws IOException, InterruptedException { TCPChannel realChannel = getRealChannel(); isConnecting = false; //if client has not registered for reads, unregister for reads //as we don't need any more data for handshake... if(log.isLoggable(Level.FINEST)) log.finest(realChannel+" about to unregister for reads"); if(!connectProxy.isClientRegistered()) { if(log.isLoggable(Level.FINEST)) log.finest(realChannel+" unregister for reads"); realChannel.unregisterForReads(); } } public void oldConnect(SocketAddress addr) { TCPChannel realChannel = getRealChannel(); if(isBlocking()) { realChannel.oldConnect(addr); } else { UtilWaitForConnect connect = new UtilWaitForConnect(); oldConnect(addr, connect); connect.waitForConnect(); } } public InetSocketAddress getRemoteAddress() { TCPChannel realChannel = getRealChannel(); return realChannel.getRemoteAddress(); } public boolean isConnected() { TCPChannel realChannel = getRealChannel(); return realChannel.isConnected(); } public TCPChannel getRealChannel() { return super.getRealChannel(); } @Override public void oldClose() { try { reader.close(); } catch(Exception e) { //TODO: this used to be AsynchronousCloseException which was okay.....fix this again... // //An SSL Channel does a write first(for SSL finish handshake) // //and if this is already closed(from far end or something else, // //it results in this exception, so ignore and move on) // return; log.log(Level.WARNING, this+"Exception on closing channel", e); } super.oldClose(); } public void oldClose(OperationCallback h) { reader.close(); TCPChannel realChannel = getRealChannel(); realChannel.oldClose(new SecProxyWriteHandler(this, h)); } public SecReaderProxy getReaderProxy() { return reader; } public SecSSLListener getConnectProxy() { return connectProxy; } public ChannelSession getSession() { TCPChannel realChannel = getRealChannel(); return realChannel.getSession(); } }