mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
281 lines
8.8 KiB
Java
281 lines
8.8 KiB
Java
/**
|
|
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
|
|
*
|
|
* This software is licensed under the GNU General Public License v3 or later.
|
|
*
|
|
* It is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or any later version.
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
package com.cloud.consoleproxy;
|
|
|
|
import java.io.BufferedInputStream;
|
|
import java.io.DataInputStream;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.net.Socket;
|
|
import java.util.StringTokenizer;
|
|
|
|
import com.cloud.console.Logger;
|
|
import com.cloud.console.RfbProto;
|
|
|
|
public class ConsoleProxyClientHandler extends Thread {
|
|
/*
|
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyClientHandler.class);
|
|
|
|
Socket clientSocket = null;
|
|
DataInputStream clientIns = null;
|
|
OutputStream clientOuts = null;
|
|
|
|
public ConsoleProxyClientHandler(Socket client) {
|
|
clientSocket = client;
|
|
}
|
|
|
|
synchronized void cleanup() {
|
|
try {
|
|
if (clientSocket != null) {
|
|
s_logger.info("Closing connection to "
|
|
+ clientSocket.getInetAddress());
|
|
|
|
clientSocket.close();
|
|
clientSocket = null;
|
|
}
|
|
} catch (IOException ioe) {
|
|
}
|
|
try {
|
|
if (clientIns != null) {
|
|
clientIns.close();
|
|
clientIns = null;
|
|
}
|
|
} catch (IOException ioe) {
|
|
}
|
|
try {
|
|
if (clientOuts != null) {
|
|
clientOuts.close();
|
|
clientOuts = null;
|
|
}
|
|
} catch (IOException ioe) {
|
|
}
|
|
}
|
|
|
|
public void run() {
|
|
try {
|
|
String srcinfo = clientSocket.getInetAddress().getHostAddress() +
|
|
":" + clientSocket.getPort();
|
|
clientIns = new DataInputStream(new BufferedInputStream(
|
|
clientSocket.getInputStream()));
|
|
// clientOuts = new GZIPOutputStream(clientSocket.getOutputStream(), 65536);
|
|
clientOuts = clientSocket.getOutputStream();
|
|
clientOuts.write("RFB 000.000\000".getBytes("US-ASCII"));
|
|
clientOuts.flush();
|
|
int b1 = clientIns.read();
|
|
int b2 = clientIns.read();
|
|
if (b1 != 'V' || b2 != 'M') {
|
|
throw new Exception ("Bad header");
|
|
}
|
|
byte[] proxyInfo = new byte[clientIns.read()];
|
|
clientIns.readFully(proxyInfo);
|
|
String proxyString = new String(proxyInfo, "US-ASCII");
|
|
StringTokenizer stk = new StringTokenizer(proxyString, ":\n");
|
|
String host = stk.nextToken();
|
|
int port = Integer.parseInt(stk.nextToken());
|
|
String sid = stk.nextToken();
|
|
ConsoleProxyViewer viewer = ConsoleProxy.getVncViewer(host, port, sid, "", "");
|
|
|
|
ConsoleProxy.waitForViewerToStart(viewer);
|
|
|
|
handleClientSession(viewer, srcinfo);
|
|
} catch (Exception ioe) {
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug(ioe.toString());
|
|
} finally {
|
|
cleanup();
|
|
}
|
|
}
|
|
|
|
void writeServer (ConsoleProxyViewer viewer, byte[] b, int off, int len) {
|
|
viewer.writeServer(b, off, len);
|
|
}
|
|
|
|
void writeClientU16 (int n) throws IOException {
|
|
byte[] b = new byte[2];
|
|
b[0] = (byte) ((n >> 8) & 0xff);
|
|
b[1] = (byte) (n & 0xff);
|
|
clientOuts.write(b);
|
|
}
|
|
|
|
void writeClientU32 (int n) throws IOException {
|
|
byte[] b = new byte[4];
|
|
b[0] = (byte) ((n >> 24) & 0xff);
|
|
b[1] = (byte) ((n >> 16) & 0xff);
|
|
b[2] = (byte) ((n >> 8) & 0xff);
|
|
b[3] = (byte) (n & 0xff);
|
|
clientOuts.write(b);
|
|
}
|
|
|
|
String RFB_VERSION_STRING = "RFB 003.008\n";
|
|
|
|
void handleClientSession(ConsoleProxyViewer viewer, String srcinfo) throws Exception {
|
|
s_logger.info("Start to handle client session");
|
|
|
|
viewer.setAjaxViewer(false);
|
|
|
|
// Exchange version with client
|
|
clientOuts.write(RFB_VERSION_STRING.getBytes("US-ASCII"));
|
|
clientOuts.flush();
|
|
byte[] clientVersion = new byte[12];
|
|
clientIns.readFully(clientVersion);
|
|
if (!RFB_VERSION_STRING.equals(new String(clientVersion, "US-ASCII"))) {
|
|
throw new Exception("Bad client version");
|
|
}
|
|
// Send security type -- no authentication needed
|
|
byte[] serverSecurity = new byte[2];
|
|
serverSecurity[0] = 1;
|
|
serverSecurity[1] = 1;
|
|
clientOuts.write(serverSecurity);
|
|
clientOuts.flush();
|
|
int clientSecurity = clientIns.read();
|
|
if (clientSecurity != 1) {
|
|
throw new Exception("Unsupported client security type " + clientSecurity);
|
|
}
|
|
byte[] serverSecResp = new byte[4];
|
|
serverSecResp[0] = serverSecResp[1] = serverSecResp[2] = serverSecResp[3] = 0;
|
|
clientOuts.write(serverSecResp);
|
|
clientOuts.flush();
|
|
|
|
// Receive and ignore client init
|
|
clientIns.read();
|
|
|
|
s_logger.info("Sending ServerInit w=" + viewer.rfb.framebufferWidth +
|
|
" h=" + viewer.rfb.framebufferHeight +
|
|
" bits=" + viewer.rfb.bitsPerPixel +
|
|
" depth=" + viewer.rfb.depth +
|
|
" name=" + viewer.rfb.desktopName);
|
|
// Send serverInit
|
|
writeClientU16(viewer.rfb.framebufferWidth);
|
|
writeClientU16(viewer.rfb.framebufferHeight);
|
|
clientOuts.write(viewer.rfb.bitsPerPixel);
|
|
clientOuts.write(viewer.rfb.depth);
|
|
clientOuts.write(viewer.rfb.bigEndian ? 1 : 0);
|
|
clientOuts.write(viewer.rfb.trueColour ? 1 : 0);
|
|
writeClientU16(viewer.rfb.redMax);
|
|
writeClientU16(viewer.rfb.greenMax);
|
|
writeClientU16(viewer.rfb.blueMax);
|
|
clientOuts.write(viewer.rfb.redShift);
|
|
clientOuts.write(viewer.rfb.greenShift);
|
|
clientOuts.write(viewer.rfb.blueShift);
|
|
byte[] pad = new byte[3];
|
|
clientOuts.write(pad);
|
|
writeClientU32(viewer.rfb.desktopName.length());
|
|
clientOuts.write(viewer.rfb.desktopName.getBytes("US-ASCII"));
|
|
clientOuts.flush();
|
|
|
|
// Lock the viewer to avoid race condition
|
|
synchronized (viewer) {
|
|
if (viewer.clientStream != null) {
|
|
s_logger.info("Disconnecting client link stream " +
|
|
viewer.clientStream.hashCode() + " from " +
|
|
viewer.clientStreamInfo);
|
|
viewer.clientStream.close();
|
|
}
|
|
viewer.clientStream = clientOuts;
|
|
viewer.clientStreamInfo = srcinfo;
|
|
viewer.lastUsedTime = System.currentTimeMillis();
|
|
|
|
s_logger.info("Setting client link stream " +
|
|
viewer.clientStream.hashCode() + " from " + srcinfo);
|
|
}
|
|
|
|
try {
|
|
while (!viewer.isDropped()) {
|
|
byte[] b = new byte[512];
|
|
int nbytes = 0;
|
|
int msgType = clientIns.read();
|
|
b[0] = (byte)msgType;
|
|
switch (msgType) {
|
|
case RfbProto.SetPixelFormat:
|
|
clientIns.readFully(b, 1, 19);
|
|
nbytes = 20;
|
|
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("C->S RFB message SetPixelFormat, size=" + nbytes);
|
|
break;
|
|
|
|
case RfbProto.SetEncodings:
|
|
|
|
clientIns.read(); // padding
|
|
b[1] = 0;
|
|
int n = clientIns.readUnsignedShort();
|
|
if (n > (512 - 4)/4) {
|
|
throw new Exception ("Too many client encodings");
|
|
}
|
|
b[2] = (byte) ((n >> 8) & 0xff);
|
|
b[3] = (byte) (n & 0xff);
|
|
clientIns.readFully(b, 4, n * 4);
|
|
nbytes = n * 4 + 4;
|
|
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("C->S RFB message SetEncodings, size=" + nbytes);
|
|
break;
|
|
|
|
case RfbProto.FramebufferUpdateRequest:
|
|
clientIns.readFully(b, 1, 9);
|
|
nbytes = 10;
|
|
|
|
if(s_logger.isDebugEnabled()) {
|
|
int i = b[1];
|
|
int x = ((0xff & b[2]) << 8) + b[3];
|
|
int y = ((0xff & b[4]) << 8) + b[5];
|
|
int w = ((0xff & b[6]) << 8) + b[7];
|
|
int h = ((0xff & b[8]) << 8) + b[9];
|
|
|
|
s_logger.debug("C->S RFB message FramebufferUpdateRequest, size=" + nbytes + " x=" + x
|
|
+" y=" + y + " w=" + w + " h=" + h);
|
|
}
|
|
break;
|
|
|
|
case RfbProto.KeyboardEvent:
|
|
clientIns.readFully(b, 1, 7);
|
|
nbytes = 8;
|
|
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("C->S RFB message KeyboardEvent, size=" + nbytes);
|
|
break;
|
|
case RfbProto.PointerEvent:
|
|
clientIns.readFully(b, 1, 5);
|
|
nbytes = 6;
|
|
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("C->S RFB message PointerEvent, size=" + nbytes);
|
|
break;
|
|
|
|
case RfbProto.VMOpsClientCustom:
|
|
clientIns.read(); // read and ignore, used to track liveliness
|
|
// of the client
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("C->S RFB message VMOpsClientCustom");
|
|
break;
|
|
|
|
default:
|
|
if(s_logger.isDebugEnabled())
|
|
s_logger.debug("C->S unknown message type: " + msgType + ", size=" + nbytes);
|
|
throw new Exception("Bad client event type: " + msgType);
|
|
}
|
|
writeServer(viewer, b, 0, nbytes);
|
|
}
|
|
} finally {
|
|
viewer.lastUsedTime = System.currentTimeMillis();
|
|
}
|
|
}
|
|
*/
|
|
}
|