Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Address review comments and sonar cloud reports
  • Loading branch information
nvazquez committed Dec 22, 2022
commit eda2a833cbe3fb05e0286fd648c2cd78c03c04f9
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import java.awt.Image;
import java.io.IOException;
import java.net.URI;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
Expand Down Expand Up @@ -81,7 +80,7 @@ public boolean isFrontEndAlive() {
return true;
}

public void sendClientFrame(Frame f) throws IOException {
public void sendClientFrame(Frame f) {
client.writeFrame(f);
}

Expand Down Expand Up @@ -245,10 +244,6 @@ private void connectClientToVNCServer(String tunnelUrl, String tunnelSession, St
ConsoleProxy.ensureRoute(getClientHostAddress());
client.connectTo(getClientHostAddress(), getClientHostPort());
}
} catch (UnknownHostException e) {
s_logger.error("Unexpected exception", e);
} catch (IOException e) {
s_logger.error("Unexpected exception", e);
} catch (Throwable e) {
s_logger.error("Unexpected exception", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.security.spec.KeySpec;
Expand All @@ -35,12 +34,11 @@
import com.cloud.consoleproxy.vnc.network.NioSocket;
import com.cloud.consoleproxy.vnc.network.NioSocketHandler;
import com.cloud.consoleproxy.vnc.network.NioSocketHandlerImpl;
import com.cloud.consoleproxy.vnc.network.SSLEngineManager;
import com.cloud.consoleproxy.vnc.network.NioSocketSSLEngineManager;
import com.cloud.consoleproxy.vnc.security.VncSecurity;
import com.cloud.consoleproxy.vnc.security.VncTLSSecurity;
import com.cloud.consoleproxy.websocket.WebSocketReverseProxy;
import com.cloud.utils.exception.CloudRuntimeException;
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.extensions.Frame;

Expand All @@ -67,7 +65,7 @@ public class NoVncClient {
public NoVncClient() {
}

public void connectTo(String host, int port, String path, String session, boolean useSSL) throws UnknownHostException, IOException {
public void connectTo(String host, int port, String path, String session, boolean useSSL) throws IOException {
if (port < 0) {
if (useSSL)
port = 443;
Expand All @@ -80,7 +78,7 @@ public void connectTo(String host, int port, String path, String session, boolea
setTunnelSocketStreams();
}

public void connectTo(String host, int port) throws UnknownHostException, IOException {
public void connectTo(String host, int port) {
// Connect to server
s_logger.info("Connecting to VNC server " + host + ":" + port + "...");
Comment thread
nvazquez marked this conversation as resolved.
Outdated
try {
Expand Down Expand Up @@ -143,16 +141,15 @@ public String handshake() throws IOException {

// Server should use RFB protocol 3.x
if (!rfbProtocol.contains(RfbConstants.RFB_PROTOCOL_VERSION_MAJOR)) {
s_logger.error("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\".");
throw new RuntimeException(
"Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\".");
String msg = "Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\".";
Comment thread
nvazquez marked this conversation as resolved.
Outdated
s_logger.error(msg);
throw new RuntimeException(msg);
}

// Proxy that we support RFB 3.3 only
return RfbConstants.RFB_PROTOCOL_VERSION + "\n";
}


/**
* VNC authentication.
*/
Expand Down Expand Up @@ -219,23 +216,27 @@ private void doVncAuth(DataInputStream in, DataOutputStream out, String password
// Read security result
int authResult = in.readInt();

String msg;
switch (authResult) {
case RfbConstants.VNC_AUTH_OK: {
// Nothing to do
break;
}

case RfbConstants.VNC_AUTH_TOO_MANY:
s_logger.error("Connection to VNC server failed: too many wrong attempts.");
throw new RuntimeException("Connection to VNC server failed: too many wrong attempts.");
msg = "Connection to VNC server failed: too many wrong attempts.";
s_logger.error(msg);
throw new RuntimeException(msg);

case RfbConstants.VNC_AUTH_FAILED:
s_logger.error("Connection to VNC server failed: wrong password.");
throw new RuntimeException("Connection to VNC server failed: wrong password.");
msg = "Connection to VNC server failed: wrong password.";
s_logger.error(msg);
throw new RuntimeException(msg);

default:
s_logger.error("Connection to VNC server failed, reason code: " + authResult);
throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult);
msg = "Connection to VNC server failed, reason code: " + authResult;
Comment thread
nvazquez marked this conversation as resolved.
Outdated
s_logger.error(msg);
throw new RuntimeException(msg);
}
}

Expand All @@ -248,8 +249,7 @@ public static byte flipByte(byte b) {
int b6_3 = (b & 0x20) >>> 3;
int b7_2 = (b & 0x40) >>> 5;
int b8_1 = (b & 0x80) >>> 7;
byte c = (byte) (b1_8 | b2_7 | b3_6 | b4_5 | b5_4 | b6_3 | b7_2 | b8_1);
return c;
return (byte) (b1_8 | b2_7 | b3_6 | b4_5 | b5_4 | b6_3 | b7_2 | b8_1);
}

public static byte[] encodePassword(byte[] challenge, String password) throws Exception {
Expand All @@ -269,49 +269,7 @@ public static byte[] encodePassword(byte[] challenge, String password) throws Ex
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);

byte[] response = cipher.doFinal(challenge);
return response;
}

/**
* Decide the RFB protocol version with the VNC server
* Reference: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#711protocolversion
*/
protected String handshakeProtocolVersion(RemoteEndpoint clientRemote) throws IOException {
// Read protocol version
byte[] buf = new byte[12];
tunnelInputStream.readFully(buf);
String rfbProtocol = new String(buf);

// Server should use RFB protocol 3.x
if (!rfbProtocol.contains(RfbConstants.RFB_PROTOCOL_VERSION_MAJOR)) {
s_logger.error("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\".");
throw new RuntimeException(
"Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\".");
}
tunnelOutputStream.write(buf);
return RfbConstants.RFB_PROTOCOL_VERSION + "\n";
}

/**
* Agree on the security type with the VNC server
* Reference: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#712security
* @return list of the security types to be processed
*/
protected List<VncSecurity> handshakeSecurityTypes(RemoteEndpoint clientRemote, String vmPassword,
String host, int port) throws IOException {
int securityType = selectFromTheServerOfferedSecurityTypes();

// Inform the server about our decision
this.tunnelOutputStream.writeByte(securityType);

byte[] numberTypesToClient = new byte[] { 1, (byte) securityType };
clientRemote.sendBytes(ByteBuffer.wrap(numberTypesToClient, 0, 2));

if (securityType == RfbConstants.V_ENCRYPT) {
securityType = getVEncryptSecuritySubtype();
}
return VncSecurity.getSecurityStack(securityType, vmPassword, host, port);
return cipher.doFinal(challenge);
}

/**
Expand Down Expand Up @@ -344,9 +302,7 @@ protected int getVEncryptSecuritySubtype() throws IOException {
}
int selectedSubtype = 0;
for (int i = 0; i < numberOfSubtypes; i++) {
while (!socketConnection.checkIfBytesAreAvailableForReading(4)) {
s_logger.trace("Waiting for vEncrypt subtype");
}
socketConnection.waitForBytesAvailableForReading(4);
int subtype = socketConnection.readUnsignedInteger(32);
if (subtype == RfbConstants.V_ENCRYPT_X509_VNC) {
selectedSubtype = subtype;
Expand All @@ -361,56 +317,6 @@ protected int getVEncryptSecuritySubtype() throws IOException {
return selectedSubtype;
}

private int selectFromTheServerOfferedSecurityTypes() throws IOException {
int numberOfSecurityTypes = tunnelInputStream.readByte();
if (numberOfSecurityTypes == 0) {
int reasonLength = tunnelInputStream.readInt();
byte[] reasonBuffer = new byte[reasonLength];
tunnelInputStream.readFully(reasonBuffer);
String reason = new String(reasonBuffer);
String errMsg = "No security type provided by the VNC server, reason: " + reason;
s_logger.error(errMsg);
throw new IOException(errMsg);
}

for (int i = 0; i < numberOfSecurityTypes; i++) {
int securityType = tunnelInputStream.readByte();
if (securityType != 0 && VncSecurity.supportedSecurityTypes.contains(securityType)) {
s_logger.info("Selected the security type: " + securityType);
return securityType;
}
}
throw new IOException("Could not select a supported or valid security type from the offered by the server");
}

/**
* VNC authentication.
*/
public void processSecurityResult(String password)
throws IOException {
// Read security result
int authResult = this.tunnelInputStream.readInt();

switch (authResult) {
case RfbConstants.VNC_AUTH_OK: {
// Nothing to do
break;
}

case RfbConstants.VNC_AUTH_TOO_MANY:
s_logger.error("Connection to VNC server failed: too many wrong attempts.");
throw new RuntimeException("Connection to VNC server failed: too many wrong attempts.");

case RfbConstants.VNC_AUTH_FAILED:
s_logger.error("Connection to VNC server failed: wrong password.");
throw new RuntimeException("Connection to VNC server failed: wrong password.");

default:
s_logger.error("Connection to VNC server failed, reason code: " + authResult);
throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult);
}
}

public int read(byte[] b) throws IOException {
return tunnelInputStream.read(b);
}
Expand Down Expand Up @@ -458,26 +364,15 @@ public void writeFrame(Frame frame) {
*/
public ByteBuffer handshakeProtocolVersion() {
ByteBuffer verStr = ByteBuffer.allocate(12);
int majorVersion;
int minorVersion;

s_logger.debug("Reading RFB protocol version");

socketConnection.readBytes(verStr, 12);

if ((new String(verStr.array())).matches("RFB \\d{3}\\.\\d{3}\\n")) {
majorVersion = Integer.parseInt((new String(verStr.array())).substring(4,7));
minorVersion = Integer.parseInt((new String(verStr.array())).substring(8,11));
} else {
throw new CloudRuntimeException("Reading version failed: not an RFB server?");
}

s_logger.info("Server supports RFB protocol version " + majorVersion + "." + minorVersion);

verStr.clear();
verStr.put(String.format("RFB %03d.%03d\n", majorVersion, minorVersion).getBytes()).flip();
String supportedRfbVersion = RfbConstants.RFB_PROTOCOL_VERSION + "\n";
verStr.put(supportedRfbVersion.getBytes()).flip();

s_logger.info("Using RFB protocol version " + majorVersion + "." + minorVersion);
setWaitForNoVnc(true);
return verStr;
}
Expand All @@ -498,9 +393,7 @@ public int handshakeSecurityType() {

List<Integer> secTypes = Arrays.asList(1, 2, 19, 261);

while (!socketConnection.checkIfBytesAreAvailableForReading(1)) {
s_logger.trace("Waiting for inStream to be ready");
}
socketConnection.waitForBytesAvailableForReading(1);
int nServerSecTypes = socketConnection.readUnsignedInteger(8);
if (nServerSecTypes == 0) {
throw new CloudRuntimeException("No security types provided by the server");
Expand All @@ -517,7 +410,7 @@ public int handshakeSecurityType() {
*/
if (secType == RfbConstants.CONNECTION_FAILED) {
for (j = secTypes.iterator(); j.hasNext(); ) {
int refType = (Integer) j.next();
int refType = j.next();
if (refType == serverSecType) {
secType = refType;
break;
Expand Down Expand Up @@ -552,9 +445,7 @@ public void processSecurityResultMsg(int secType) {
if (secType == RfbConstants.NO_AUTH) {
result = RfbConstants.VNC_AUTH_OK;
} else {
while (!socketConnection.checkIfBytesAreAvailableForReading(1)) {
s_logger.trace("Waiting for inStream");
}
socketConnection.waitForBytesAvailableForReading(1);
result = socketConnection.readUnsignedInteger(32);
}

Expand Down Expand Up @@ -608,7 +499,7 @@ public void processHandshakeSecurityType(int secType, String vmPassword, String
security.process(this.socketConnection);
if (security instanceof VncTLSSecurity) {
s_logger.debug("Setting new streams with SSLEngineManger after TLS security has passed");
SSLEngineManager sslEngineManager = ((VncTLSSecurity) security).getSSLEngineManager();
NioSocketSSLEngineManager sslEngineManager = ((VncTLSSecurity) security).getSSLEngineManager();
socketConnection.startTLSConnection(sslEngineManager);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NioSocket {
Expand All @@ -33,6 +32,7 @@ public class NioSocket {
private Selector writeSelector;
private Selector readSelector;

private static final int connectionTimeoutMillis = 3000;
private static final Logger s_logger = Logger.getLogger(NioSocket.class);

private void initializeSocket() {
Expand All @@ -49,33 +49,33 @@ private void initializeSocket() {
}
}

private void waitForSocketSelectorConnected(Selector selector) {
try {
while (selector.select(connectionTimeoutMillis) <= 0) {
s_logger.debug("Waiting for ready operations to connect to the socket");
}
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey selectionKey: keys) {
if (selectionKey.isConnectable()) {
if (socketChannel.isConnectionPending()) {
socketChannel.finishConnect();
}
s_logger.debug("Connected to the socket");
break;
}
}
} catch (IOException e) {
s_logger.error(String.format("Error waiting for socket selector ready: %s", e.getMessage()), e);
}
}

private void connectSocket(String host, int port) {
try {
socketChannel.connect(new InetSocketAddress(host, port));
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
boolean connected = false;
while (selector.select(3000) > 0) {
while (!connected) {
Set keys = selector.selectedKeys();
Iterator i = keys.iterator();

while (i.hasNext()) {
SelectionKey key = (SelectionKey)i.next();

// Remove the current key
i.remove();

// Attempt a connection
if (key.isConnectable()) {
if (socketChannel.isConnectionPending()) {
socketChannel.finishConnect();
}
connected = true;
}
}
}
}
waitForSocketSelectorConnected(selector);
socketChannel.socket().setTcpNoDelay(false);
} catch (IOException e) {
s_logger.error(String.format("Error creating NioSocket to %s:%s: %s", host, port, e.getMessage()), e);
Expand Down
Loading