Files
2026-01-29 11:29:56 +08:00

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();
}
}