185 lines
5.1 KiB
Java
185 lines
5.1 KiB
Java
/* Copyright (C) 2012,2014 Brian P. Hinz
|
|
* Copyright (C) 2012 D. R. Commander. All Rights Reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
package com.tigervnc.network;
|
|
|
|
import java.io.*;
|
|
import java.nio.*;
|
|
import java.nio.channels.*;
|
|
import javax.net.ssl.*;
|
|
import javax.net.ssl.SSLEngineResult.*;
|
|
import java.util.concurrent.Executor;
|
|
import java.util.concurrent.Executors;
|
|
|
|
import com.tigervnc.rdr.FdInStream;
|
|
import com.tigervnc.rdr.FdOutStream;
|
|
|
|
public class SSLEngineManager {
|
|
|
|
private SSLEngine engine = null;
|
|
|
|
private ByteBuffer myNetData;
|
|
private ByteBuffer peerNetData;
|
|
|
|
private Executor executor;
|
|
private FdInStream in;
|
|
private FdOutStream os;
|
|
|
|
public SSLEngineManager(SSLEngine sslEngine, FdInStream is_,
|
|
FdOutStream os_) throws IOException {
|
|
|
|
in = is_;
|
|
os = os_;
|
|
engine = sslEngine;
|
|
|
|
executor = Executors.newSingleThreadExecutor();
|
|
|
|
int pktBufSize = engine.getSession().getPacketBufferSize();
|
|
myNetData = ByteBuffer.allocate(pktBufSize);
|
|
peerNetData = ByteBuffer.allocate(pktBufSize);
|
|
}
|
|
|
|
public void doHandshake() throws Exception {
|
|
|
|
// Begin handshake
|
|
engine.beginHandshake();
|
|
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
|
|
|
|
// Process handshaking message
|
|
SSLEngineResult res = null;
|
|
int appBufSize = engine.getSession().getApplicationBufferSize();
|
|
ByteBuffer peerAppData = ByteBuffer.allocate(appBufSize);
|
|
ByteBuffer myAppData = ByteBuffer.allocate(appBufSize);
|
|
while (hs != SSLEngineResult.HandshakeStatus.FINISHED &&
|
|
hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
|
|
|
|
switch (hs) {
|
|
|
|
case NEED_UNWRAP:
|
|
// Receive handshaking data from peer
|
|
peerNetData.flip();
|
|
res = engine.unwrap(peerNetData, peerAppData);
|
|
peerNetData.compact();
|
|
hs = res.getHandshakeStatus();
|
|
|
|
// Check status
|
|
switch (res.getStatus()) {
|
|
case BUFFER_UNDERFLOW:
|
|
int avail = in.check(1, peerNetData.remaining(), false);
|
|
in.readBytes(peerNetData, avail);
|
|
break;
|
|
case OK:
|
|
// Process incoming handshaking data
|
|
break;
|
|
case CLOSED:
|
|
engine.closeInbound();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case NEED_WRAP:
|
|
// Generate handshaking data
|
|
res = engine.wrap(myAppData, myNetData);
|
|
hs = res.getHandshakeStatus();
|
|
|
|
// Check status
|
|
switch (res.getStatus()) {
|
|
case OK:
|
|
myNetData.flip();
|
|
os.writeBytes(myNetData, myNetData.remaining());
|
|
os.flush();
|
|
myNetData.compact();
|
|
break;
|
|
case CLOSED:
|
|
engine.closeOutbound();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case NEED_TASK:
|
|
// Handle blocking tasks
|
|
executeTasks();
|
|
break;
|
|
}
|
|
hs = engine.getHandshakeStatus();
|
|
}
|
|
}
|
|
|
|
private void executeTasks() {
|
|
Runnable task;
|
|
while ((task = engine.getDelegatedTask()) != null) {
|
|
executor.execute(task);
|
|
}
|
|
}
|
|
|
|
public int read(ByteBuffer data, int length) throws IOException {
|
|
// Read SSL/TLS encoded data from peer
|
|
peerNetData.flip();
|
|
SSLEngineResult res = engine.unwrap(peerNetData, data);
|
|
peerNetData.compact();
|
|
switch (res.getStatus()) {
|
|
case OK :
|
|
return res.bytesProduced();
|
|
|
|
case BUFFER_UNDERFLOW:
|
|
// attempt to drain the underlying buffer first
|
|
int need = peerNetData.remaining();
|
|
in.readBytes(peerNetData, in.check(1, need, true));
|
|
break;
|
|
|
|
case CLOSED:
|
|
engine.closeInbound();
|
|
break;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public int write(ByteBuffer data, int length) throws IOException {
|
|
int n = 0;
|
|
while (data.hasRemaining()) {
|
|
SSLEngineResult res = engine.wrap(data, myNetData);
|
|
n += res.bytesConsumed();
|
|
switch (res.getStatus()) {
|
|
case OK:
|
|
myNetData.flip();
|
|
os.writeBytes(myNetData, myNetData.remaining());
|
|
os.flush();
|
|
myNetData.compact();
|
|
break;
|
|
|
|
case BUFFER_OVERFLOW:
|
|
// Make room in the buffer by flushing the outstream
|
|
myNetData.flip();
|
|
os.writeBytes(myNetData, myNetData.remaining());
|
|
myNetData.compact();
|
|
break;
|
|
|
|
case CLOSED:
|
|
engine.closeOutbound();
|
|
break;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
public SSLSession getSession() {
|
|
return engine.getSession();
|
|
}
|
|
|
|
}
|