You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by de...@apache.org on 2013/12/23 10:12:56 UTC
[02/22] CLOUDSTACK-5344: Update to allow rdp console to access
hyper-v vm virtual framebuffer.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/EncodingsMessage.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/EncodingsMessage.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/EncodingsMessage.java
new file mode 100755
index 0000000..5dfa831
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/EncodingsMessage.java
@@ -0,0 +1,63 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package vncclient.vnc;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+
+public class EncodingsMessage extends BaseElement {
+
+ protected final int[] encodings;
+
+ public EncodingsMessage(String id, int[] encodings) {
+ super(id);
+ this.encodings = encodings;
+ declarePads();
+ }
+
+ protected void declarePads() {
+ inputPads.put(STDIN, null);
+ outputPads.put(STDOUT, null);
+ }
+
+ @Override
+ public void handleData(ByteBuffer buf, Link link) {
+ if (buf == null)
+ return;
+
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+ buf.unref();
+
+ ByteBuffer outBuf = new ByteBuffer(4 + encodings.length * 4);
+
+ outBuf.writeByte(RfbConstants.CLIENT_SET_ENCODINGS);
+
+ outBuf.writeByte(0);// padding
+
+ outBuf.writeShort(encodings.length);
+
+ for (int i = 0; i < encodings.length; i++) {
+ outBuf.writeInt(encodings[i]);
+ }
+
+ pushDataToAllOuts(outBuf);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/FrameBufferUpdateRequest.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/FrameBufferUpdateRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/FrameBufferUpdateRequest.java
new file mode 100755
index 0000000..446aa54
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/FrameBufferUpdateRequest.java
@@ -0,0 +1,127 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package vncclient.vnc;
+
+import common.ScreenDescription;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+import streamer.debug.MockSink;
+import streamer.debug.MockSource;
+
+public class FrameBufferUpdateRequest extends BaseElement {
+ // TODO: use object with fields instead of raw values in map
+ public static final String INCREMENTAL_UPDATE = "incremental";
+ public static final String TARGET_X = "x";
+ public static final String TARGET_Y = "y";
+ public static final String WIDTH = "width";
+ public static final String HEIGHT = "height";
+
+ protected ScreenDescription screen;
+
+ public FrameBufferUpdateRequest(String id, ScreenDescription screen) {
+ super(id);
+ this.screen = screen;
+ }
+
+ @Override
+ public void handleData(ByteBuffer buf, Link link) {
+ if (buf == null)
+ return;
+
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+ Boolean incremental = (Boolean)buf.getMetadata(INCREMENTAL_UPDATE);
+ Integer x = (Integer)buf.getMetadata(TARGET_X);
+ Integer y = (Integer)buf.getMetadata(TARGET_Y);
+ Integer width = (Integer)buf.getMetadata(WIDTH);
+ Integer height = (Integer)buf.getMetadata(HEIGHT);
+ buf.unref();
+
+ // Set default values when parameters are not set
+ if (incremental == null)
+ incremental = false;
+
+ if (x == null)
+ x = 0;
+ if (y == null)
+ y = 0;
+
+ if (width == null)
+ width = screen.getFramebufferWidth();
+ if (height == null)
+ height = screen.getFramebufferHeight();
+
+ ByteBuffer outBuf = new ByteBuffer(10);
+
+ outBuf.writeByte(RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST);
+ outBuf.writeByte((incremental) ? RfbConstants.FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST : RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST);
+ outBuf.writeShort(x);
+ outBuf.writeShort(y);
+ outBuf.writeShort(width);
+ outBuf.writeShort(height);
+
+ if (verbose) {
+ outBuf.putMetadata("sender", this);
+ outBuf.putMetadata("dimensions", width + "x" + height + "@" + x + "x" + y);
+ }
+
+ pushDataToAllOuts(outBuf);
+ }
+
+ public static void main(String args[]) {
+ System.setProperty("streamer.Element.debug", "true");
+
+ ScreenDescription screen = new ScreenDescription();
+ screen.setFramebufferSize(120, 80);
+ Element adapter = new FrameBufferUpdateRequest("renderer", screen);
+
+ Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {
+ // Request
+ RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST,
+ // Full update (redraw area)
+ RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST,
+ // X
+ 0, 1,
+ // Y
+ 0, 2,
+ // Width
+ 0, 3,
+ // Height
+ 0, 4}));
+
+ ByteBuffer buf = new ByteBuffer(new byte[0]);
+ buf.putMetadata(TARGET_X, 1);
+ buf.putMetadata(TARGET_Y, 2);
+ buf.putMetadata(WIDTH, 3);
+ buf.putMetadata(HEIGHT, 4);
+
+ Element source = new MockSource("source", new ByteBuffer[] {buf});
+
+ Pipeline pipeline = new PipelineImpl("test");
+
+ pipeline.addAndLink(source, adapter, sink);
+ pipeline.runMainLoop("source", STDOUT, false, false);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RGB888LE32PixelFormatRequest.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RGB888LE32PixelFormatRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RGB888LE32PixelFormatRequest.java
new file mode 100755
index 0000000..d534f82
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RGB888LE32PixelFormatRequest.java
@@ -0,0 +1,90 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package vncclient.vnc;
+
+import common.ScreenDescription;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+
+public class RGB888LE32PixelFormatRequest extends BaseElement {
+ protected int bitsPerPixel = 32;
+ protected int depth = 24;
+ protected int bigEndianFlag = RfbConstants.LITTLE_ENDIAN;
+ protected int trueColourFlag = RfbConstants.TRUE_COLOR;
+ protected int redMax = 255;
+ protected int greenMax = 255;
+ protected int blueMax = 255;
+ protected int redShift = 0;
+ protected int greenShift = 8;
+ protected int blueShift = 16;
+
+ protected ScreenDescription screen;
+
+ public RGB888LE32PixelFormatRequest(String id, ScreenDescription screen) {
+ super(id);
+ this.screen = screen;
+ }
+
+ protected void declarePads() {
+ inputPads.put(STDIN, null);
+ outputPads.put(STDOUT, null);
+ }
+
+ @Override
+ public void handleData(ByteBuffer buf, Link link) {
+ if (buf == null)
+ return;
+
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+ buf.unref();
+
+ ByteBuffer outBuf = new ByteBuffer(20);
+
+ outBuf.writeByte(RfbConstants.CLIENT_SET_PIXEL_FORMAT);
+
+ // Padding
+ outBuf.writeByte(0);
+ outBuf.writeByte(0);
+ outBuf.writeByte(0);
+
+ // Send pixel format
+ outBuf.writeByte(bitsPerPixel);
+ outBuf.writeByte(depth);
+ outBuf.writeByte(bigEndianFlag);
+ outBuf.writeByte(trueColourFlag);
+ outBuf.writeShort(redMax);
+ outBuf.writeShort(greenMax);
+ outBuf.writeShort(blueMax);
+ outBuf.writeByte(redShift);
+ outBuf.writeByte(greenShift);
+ outBuf.writeByte(blueShift);
+
+ // Padding
+ outBuf.writeByte(0);
+ outBuf.writeByte(0);
+ outBuf.writeByte(0);
+
+ screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag != RfbConstants.LITTLE_ENDIAN, trueColourFlag == RfbConstants.TRUE_COLOR, redMax, greenMax,
+ blueMax, redShift, greenShift, blueShift);
+
+ pushDataToAllOuts(outBuf);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RfbConstants.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RfbConstants.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RfbConstants.java
new file mode 100755
index 0000000..a3895d4
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RfbConstants.java
@@ -0,0 +1,85 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package vncclient.vnc;
+
+import java.nio.charset.Charset;
+
+public interface RfbConstants {
+
+ public static final String RFB_PROTOCOL_VERSION_MAJOR = "RFB 003.";
+ public static final String VNC_PROTOCOL_VERSION_MINOR = "003";
+ public static final String RFB_PROTOCOL_VERSION = RFB_PROTOCOL_VERSION_MAJOR + VNC_PROTOCOL_VERSION_MINOR;
+
+ /**
+ * Server message types.
+ */
+ final static int SERVER_FRAMEBUFFER_UPDATE = 0, SERVER_SET_COLOURMAP_ENTRIES = 1, SERVER_BELL = 2, SERVER_CUT_TEXT = 3;
+
+ /**
+ * Client message types.
+ */
+ public static final int CLIENT_SET_PIXEL_FORMAT = 0, CLIENT_FIX_COLOURMAP_ENTRIES = 1, CLIENT_SET_ENCODINGS = 2, CLIENT_FRAMEBUFFER_UPDATE_REQUEST = 3,
+ CLIENT_KEYBOARD_EVENT = 4, CLIENT_POINTER_EVENT = 5, CLIENT_CUT_TEXT = 6;
+
+ /**
+ * Server authorization type
+ */
+ public final static int CONNECTION_FAILED = 0, NO_AUTH = 1, VNC_AUTH = 2;
+
+ /**
+ * Server authorization reply.
+ */
+ public final static int VNC_AUTH_OK = 0, VNC_AUTH_FAILED = 1, VNC_AUTH_TOO_MANY = 2;
+
+ /**
+ * Encodings.
+ */
+ public final static int ENCODING_RAW = 0, ENCODING_COPY_RECT = 1, ENCODING_RRE = 2, ENCODING_CO_RRE = 4, ENCODING_HEXTILE = 5, ENCODING_ZRLE = 16;
+
+ /**
+ * Pseudo-encodings.
+ */
+ public final static int ENCODING_CURSOR = -239 /*0xFFFFFF11*/, ENCODING_DESKTOP_SIZE = -223 /*0xFFFFFF21*/;
+
+ /**
+ * Encodings, which we support.
+ */
+ public final static int[] SUPPORTED_ENCODINGS_ARRAY = {ENCODING_RAW, ENCODING_COPY_RECT, ENCODING_DESKTOP_SIZE};
+
+ /**
+ * Frame buffer update request type: update of whole screen or partial update.
+ */
+ public static final int FRAMEBUFFER_FULL_UPDATE_REQUEST = 0, FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST = 1;
+
+ public static final int KEY_UP = 0, KEY_DOWN = 1;
+
+ public static final int LITTLE_ENDIAN = 0, BIG_ENDIAN = 1;
+
+ public static final int EXCLUSIVE_ACCESS = 0, SHARED_ACCESS = 1;
+
+ public static final int PALETTE = 0, TRUE_COLOR = 1;
+
+ /**
+ * Default 8 bit charset to use when communicating with server.
+ */
+ public static final Charset US_ASCII_CHARSET = Charset.availableCharsets().get("US-ASCII");
+
+ /**
+ * Default 16 bit charset to use when communicating with server.
+ */
+ public static final Charset UCS2_CHARSET = Charset.availableCharsets().get("UTF-16LE");
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncInitializer.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncInitializer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncInitializer.java
new file mode 100755
index 0000000..b3bd176
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncInitializer.java
@@ -0,0 +1,245 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package vncclient.vnc;
+
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.OneTimeSwitch;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+import streamer.debug.MockSink;
+import streamer.debug.MockSource;
+
+import common.ScreenDescription;
+
+public class VncInitializer extends OneTimeSwitch {
+
+ // Pad names
+ public static final String CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD = "encodings";
+ public static final String CLIENT_PIXEL_FORMAT_ADAPTER_PAD = "pixel_format";
+
+ protected byte sharedFlag = RfbConstants.EXCLUSIVE_ACCESS;
+
+ /**
+ * Properties of remote screen .
+ */
+ protected ScreenDescription screen;
+
+ public VncInitializer(String id, boolean shared, ScreenDescription screen) {
+ super(id);
+
+ setSharedFlag(shared);
+ this.screen = screen;
+
+ declarePads();
+ }
+
+ @Override
+ protected void declarePads() {
+ super.declarePads();
+ outputPads.put(CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD, null);
+ outputPads.put(CLIENT_PIXEL_FORMAT_ADAPTER_PAD, null);
+ }
+
+ public ScreenDescription getScreen() {
+ return screen;
+ }
+
+ public void setScreen(ScreenDescription screen) {
+ this.screen = screen;
+ }
+
+ public void setSharedFlag(boolean shared) {
+ if (shared)
+ sharedFlag = RfbConstants.SHARED_ACCESS;
+ else
+ sharedFlag = RfbConstants.EXCLUSIVE_ACCESS;
+ }
+
+ @Override
+ protected void handleOneTimeData(ByteBuffer buf, Link link) {
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+ // Server initialization message is at least 24 bytes long + length of
+ // desktop name
+ if (!cap(buf, 24, UNLIMITED, link, false))
+ return;
+
+ // Read server initialization message
+ // Read frame buffer size
+ int framebufferWidth = buf.readUnsignedShort();
+ int framebufferHeight = buf.readUnsignedShort();
+
+ // Read pixel format
+ int bitsPerPixel = buf.readUnsignedByte();
+ int depth = buf.readUnsignedByte();
+
+ int bigEndianFlag = buf.readUnsignedByte();
+ int trueColorFlag = buf.readUnsignedByte();
+
+ int redMax = buf.readUnsignedShort();
+ int greenMax = buf.readUnsignedShort();
+ int blueMax = buf.readUnsignedShort();
+
+ int redShift = buf.readUnsignedByte();
+ int greenShift = buf.readUnsignedByte();
+ int blueShift = buf.readUnsignedByte();
+
+ // Skip padding
+ buf.skipBytes(3);
+
+ // Read desktop name
+ int length = buf.readSignedInt();
+
+ // Consume exactly $length bytes, push back any extra bytes
+ if (!cap(buf, length, length, link, true))
+ return;
+
+ String desktopName = buf.readString(length, RfbConstants.US_ASCII_CHARSET);
+ buf.unref();
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Desktop name: \"" + desktopName + "\", bpp: " + bitsPerPixel + ", depth: " + depth + ", screen size: "
+ + framebufferWidth + "x" + framebufferHeight + ".");
+
+ // Set screen properties
+ screen.setFramebufferSize(framebufferWidth, framebufferHeight);
+ screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag != RfbConstants.LITTLE_ENDIAN, trueColorFlag == RfbConstants.TRUE_COLOR, redMax, greenMax, blueMax, redShift,
+ greenShift, blueShift);
+ screen.setDesktopName(desktopName);
+
+ // If sever screen has different parameters than ours, then change it
+ if (!screen.isRGB888_32_LE()) {
+ // Send client pixel format
+ sendClientPixelFormat();
+ }
+
+ // Send encodings supported by client
+ sendSupportedEncodings();
+
+ switchOff();
+
+ }
+
+ @Override
+ protected void onStart() {
+ ByteBuffer buf = new ByteBuffer(new byte[] {sharedFlag});
+ pushDataToOTOut(buf);
+ }
+
+ private void sendClientPixelFormat() {
+ pushDataToPad(CLIENT_PIXEL_FORMAT_ADAPTER_PAD, new ByteBuffer(0));
+ }
+
+ private void sendSupportedEncodings() {
+ pushDataToPad(CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD, new ByteBuffer(0));
+ }
+
+ public String toString() {
+ return "VncInit(" + id + ")";
+ }
+
+ /**
+ * Example.
+ */
+ public static void main(String args[]) {
+ // System.setProperty("streamer.Link.debug", "true");
+ System.setProperty("streamer.Element.debug", "true");
+ // System.setProperty("streamer.Pipeline.debug", "true");
+
+ final String desktopName = "test";
+
+ Element source = new MockSource("source") {
+ {
+ bufs = ByteBuffer.convertByteArraysToByteBuffers(
+ // Send screen description
+ new byte[] {
+ // Framebuffer width (short)
+ 0, (byte)200,
+ // Framebuffer height (short)
+ 0, 100,
+ // Bits per pixel
+ 32,
+ // Depth,
+ 24,
+ // Endianness flag
+ RfbConstants.LITTLE_ENDIAN,
+ // Truecolor flag
+ RfbConstants.TRUE_COLOR,
+ // Red max (short)
+ 0, (byte)255,
+ // Green max (short)
+ 0, (byte)255,
+ // Blue max (short)
+ 0, (byte)255,
+ // Red shift
+ 16,
+ // Green shift
+ 8,
+ // Blue shift
+ 0,
+ // Padding
+ 0, 0, 0,
+ // Desktop name length (int)
+ 0, 0, 0, 4,
+ // Desktop name ("test", 4 bytes)
+ 't', 'e', 's', 't',
+
+ // Tail
+ 1, 2, 3
+
+ },
+ // Tail packet
+ new byte[] {4, 5, 6});
+ }
+ };
+
+ ScreenDescription screen = new ScreenDescription();
+ final VncInitializer init = new VncInitializer("init", true, screen);
+ Element initSink = new MockSink("initSink") {
+ {
+ // Expect shared flag
+ bufs = ByteBuffer.convertByteArraysToByteBuffers(new byte[] {RfbConstants.SHARED_ACCESS});
+ }
+ };
+ Element mainSink = new MockSink("mainSink") {
+ {
+ // Expect two tail packets
+ bufs = ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3}, new byte[] {4, 5, 6});
+ }
+ };
+ ByteBuffer[] emptyBuf = ByteBuffer.convertByteArraysToByteBuffers(new byte[] {});
+ Element encodingsSink = new MockSink("encodings", emptyBuf);
+ Element pixelFormatSink = new MockSink("pixel_format", emptyBuf);
+
+ Pipeline pipeline = new PipelineImpl("test");
+ pipeline.addAndLink(source, init, mainSink);
+ pipeline.add(encodingsSink, pixelFormatSink, initSink);
+ pipeline.link("init >otout", "initSink");
+ pipeline.link("init >" + CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD, "encodings");
+ pipeline.link("init >" + CLIENT_PIXEL_FORMAT_ADAPTER_PAD, "pixel_format");
+
+ pipeline.runMainLoop("source", STDOUT, false, false);
+
+ if (!screen.isRGB888_32_LE())
+ System.err.println("Screen description was read incorrectly: " + screen + ".");
+ if (!desktopName.equals(screen.getDesktopName()))
+ System.err.println("Screen desktop name was read incorrectly: \"" + screen.getDesktopName() + "\".");
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncMessageHandler.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncMessageHandler.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncMessageHandler.java
new file mode 100755
index 0000000..845ed3a
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncMessageHandler.java
@@ -0,0 +1,419 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package vncclient.vnc;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+import streamer.debug.MockSink;
+import streamer.debug.MockSource;
+
+import common.BitmapOrder;
+import common.BitmapRectangle;
+import common.CopyRectOrder;
+import common.ScreenDescription;
+import common.adapter.AwtClipboardAdapter;
+
+public class VncMessageHandler extends BaseElement {
+ protected ScreenDescription screen = null;
+
+ // Pad names
+ public static final String SERVER_BELL_ADAPTER_PAD = "bell";
+ public static final String SERVER_CLIPBOARD_ADAPTER_PAD = "clipboard";
+ public static final String PIXEL_ADAPTER_PAD = "pixels";
+ public static final String FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD = "fbur";
+
+ // Keys for metadata
+ public static final String TARGET_X = "x";
+ public static final String TARGET_Y = "y";
+ public static final String WIDTH = "width";
+ public static final String HEIGHT = "height";
+ public static final String SOURCE_X = "srcX";
+ public static final String SOURCE_Y = "srcY";
+ public static final String PIXEL_FORMAT = "pixel_format";
+
+ private static final String NUM_OF_PROCESSED_RECTANGLES = "rects";
+ private static final String SAVED_CURSOR_POSITION = "cursor";
+
+ // Pixel format: RGB888 LE 32
+ public static final String RGB888LE32 = "RGB888LE32";
+
+ public VncMessageHandler(String id, ScreenDescription screen) {
+ super(id);
+ this.screen = screen;
+ declarePads();
+ }
+
+ private void declarePads() {
+ outputPads.put(SERVER_BELL_ADAPTER_PAD, null);
+ outputPads.put(SERVER_BELL_ADAPTER_PAD, null);
+ outputPads.put(SERVER_CLIPBOARD_ADAPTER_PAD, null);
+ outputPads.put(PIXEL_ADAPTER_PAD, null);
+ outputPads.put(FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD, null);
+
+ inputPads.put("stdin", null);
+ }
+
+ @Override
+ public void handleData(ByteBuffer buf, Link link) {
+ if (buf == null)
+ return;
+
+ try {
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+ if (!cap(buf, 1, UNLIMITED, link, false))
+ return;
+
+ // Read server message type
+ int messageType = buf.readUnsignedByte();
+
+ // Invoke packet handler by packet type.
+ switch (messageType) {
+
+ case RfbConstants.SERVER_FRAMEBUFFER_UPDATE: {
+ // Handle frame buffer update
+ if (!handleFBU(buf, link))
+ return;
+
+ // Frame buffer update is received and fully processed, send request for
+ // another frame buffer update to server.
+ sendFBUR();
+
+ break;
+ }
+
+ case RfbConstants.SERVER_BELL: {
+ if (!handleBell(buf, link))
+ return;
+ break;
+ }
+
+ case RfbConstants.SERVER_CUT_TEXT: {
+ if (!handleClipboard(buf, link))
+ return;
+ break;
+ }
+
+ default:
+ // TODO: allow to extend functionality
+ throw new RuntimeException("Unknown server packet type: " + messageType + ".");
+ }
+
+ // Cut tail, if any
+ cap(buf, 0, 0, link, true);
+ } finally {
+
+ // Return processed buffer back to pool
+ buf.unref();
+ }
+ }
+
+ private boolean handleClipboard(ByteBuffer buf, Link link) {
+ if (!cap(buf, 3 + 4, UNLIMITED, link, true))
+ return false;
+
+ // Skip padding
+ buf.skipBytes(3);
+
+ // Read text length
+ int length = buf.readSignedInt();
+
+ // We need full string to parse it
+ if (!cap(buf, length, UNLIMITED, link, true))
+ return false;
+
+ String content = buf.readString(length, RfbConstants.US_ASCII_CHARSET);
+
+ // Send content in metadata
+ ByteBuffer outBuf = new ByteBuffer(0);
+ outBuf.putMetadata(AwtClipboardAdapter.CLIPBOARD_CONTENT, content);
+
+ pushDataToPad(SERVER_CLIPBOARD_ADAPTER_PAD, outBuf);
+
+ return true;
+ }
+
+ private boolean handleBell(ByteBuffer buf, Link link) {
+ // Send empty packet to bell adapter to produce bell
+ pushDataToPad(SERVER_BELL_ADAPTER_PAD, new ByteBuffer(0));
+
+ return true;
+ }
+
+ // FIXME: this method is too complex
+ private boolean handleFBU(ByteBuffer buf, Link link) {
+
+ // We need at least 3 bytes here, 1 - padding, 2 - number of rectangles
+ if (!cap(buf, 3, UNLIMITED, link, true))
+ return false;
+
+ buf.skipBytes(1);// Skip padding
+
+ // Read number of rectangles
+ int numberOfRectangles = buf.readUnsignedShort();
+
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Frame buffer update. Number of rectangles: " + numberOfRectangles + ".");
+
+ // Each rectangle must have header at least, header length is 12 bytes.
+ if (!cap(buf, 12 * numberOfRectangles, UNLIMITED, link, true))
+ return false;
+
+ // For all rectangles
+
+ // Restore saved point, to avoid flickering and performance problems when
+ // frame buffer update is split between few incoming packets.
+ int numberOfProcessedRectangles = (buf.getMetadata(NUM_OF_PROCESSED_RECTANGLES) != null) ? (Integer)buf.getMetadata(NUM_OF_PROCESSED_RECTANGLES) : 0;
+ if (buf.getMetadata(SAVED_CURSOR_POSITION) != null)
+ buf.cursor = (Integer)buf.getMetadata(SAVED_CURSOR_POSITION);
+
+ if (verbose && numberOfProcessedRectangles > 0)
+ System.out.println("[" + this + "] INFO: Restarting from saved point. Number of already processed rectangles: " + numberOfRectangles + ", cursor: "
+ + buf.cursor + ".");
+
+ // For all new rectangles
+ for (int i = numberOfProcessedRectangles; i < numberOfRectangles; i++) {
+
+ // We need coordinates of rectangle (2x4 bytes) and encoding type (4
+ // bytes)
+ if (!cap(buf, 12, UNLIMITED, link, true))
+ return false;
+
+ // Read coordinates of rectangle
+ int x = buf.readUnsignedShort();
+ int y = buf.readUnsignedShort();
+ int width = buf.readUnsignedShort();
+ int height = buf.readUnsignedShort();
+
+ // Read rectangle encoding
+ int encodingType = buf.readSignedInt();
+
+ // Process rectangle
+ switch (encodingType) {
+
+ case RfbConstants.ENCODING_RAW: {
+ if (!handleRawRectangle(buf, link, x, y, width, height))
+ return false;
+ break;
+ }
+
+ case RfbConstants.ENCODING_COPY_RECT: {
+ if (!handleCopyRect(buf, link, x, y, width, height))
+ return false;
+ break;
+ }
+
+ case RfbConstants.ENCODING_DESKTOP_SIZE: {
+ if (!handleScreenSizeChangeRect(buf, link, x, y, width, height))
+ return false;
+ break;
+ }
+
+ default:
+ // TODO: allow to extend functionality
+ throw new RuntimeException("Unsupported ecnoding: " + encodingType + ".");
+ }
+
+ // Update information about processed rectangles to avoid handling of same
+ // rectangle multiple times.
+ // TODO: push back partial rectangle only instead
+ buf.putMetadata(NUM_OF_PROCESSED_RECTANGLES, ++numberOfProcessedRectangles);
+ buf.putMetadata(SAVED_CURSOR_POSITION, buf.cursor);
+ }
+
+ return true;
+ }
+
+ private boolean handleScreenSizeChangeRect(ByteBuffer buf, Link link, int x, int y, int width, int height) {
+ // Remote screen size is changed
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Screen size rect. Width: " + width + ", height: " + height + ".");
+
+ screen.setFramebufferSize(width, height);
+
+ return true;
+ }
+
+ private boolean handleCopyRect(ByteBuffer buf, Link link, int x, int y, int width, int height) {
+ // Copy rectangle from one part of screen to another.
+ // Areas may overlap. Antialiasing may cause visible artifacts.
+
+ // We need 4 bytes with coordinates of source rectangle
+ if (!cap(buf, 4, UNLIMITED, link, true))
+ return false;
+
+ CopyRectOrder order = new CopyRectOrder();
+
+ order.srcX = buf.readUnsignedShort();
+ order.srcY = buf.readUnsignedShort();
+ order.x = x;
+ order.y = y;
+ order.width = width;
+ order.height = height;
+
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Copy rect. X: " + x + ", y: " + y + ", width: " + width + ", height: " + height + ", srcX: " + order.srcX
+ + ", srcY: " + order.srcY + ".");
+
+ pushDataToPad(PIXEL_ADAPTER_PAD, new ByteBuffer(order));
+
+ return true;
+ }
+
+ private boolean handleRawRectangle(ByteBuffer buf, Link link, int x, int y, int width, int height) {
+ // Raw rectangle is just array of pixels to draw on screen.
+ int rectDataLength = width * height * screen.getBytesPerPixel();
+
+ // We need at least rectDataLength bytes. Extra bytes may contain other
+ // rectangles.
+ if (!cap(buf, rectDataLength, UNLIMITED, link, true))
+ return false;
+
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Raw rect. X: " + x + ", y: " + y + ", width: " + width + ", height: " + height + ", data length: "
+ + rectDataLength + ".");
+
+ BitmapRectangle rectangle = new BitmapRectangle();
+ rectangle.x = x;
+ rectangle.y = y;
+ rectangle.width = width;
+ rectangle.height = height;
+ rectangle.bufferWidth = width;
+ rectangle.bufferHeight = height;
+ rectangle.bitmapDataStream = buf.readBytes(rectDataLength);
+ rectangle.colorDepth = screen.getColorDeph();
+
+ BitmapOrder order = new BitmapOrder();
+ order.rectangles = new BitmapRectangle[] {rectangle};
+
+ pushDataToPad(PIXEL_ADAPTER_PAD, new ByteBuffer(order));
+ return true;
+ }
+
+ public void onStart() {
+ // Send Frame Buffer Update request
+ sendFBUR();
+ }
+
+ private void sendFBUR() {
+ ByteBuffer buf = new ByteBuffer(0);
+ buf.putMetadata("incremental", true);
+ pushDataToPad(FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD, buf);
+ }
+
+ public String toString() {
+ return "VNCMessageHandler(" + id + ")";
+ }
+
+ /**
+ * Example.
+ */
+ public static void main(String[] args) {
+
+ // System.setProperty("streamer.Link.debug", "true");
+ System.setProperty("streamer.Element.debug", "true");
+ // System.setProperty("streamer.Pipeline.debug", "true");
+
+ Element source = new MockSource("source") {
+ {
+ // Split messages at random boundaries to check "pushback" logic
+ bufs = ByteBuffer.convertByteArraysToByteBuffers(new byte[] {
+ // Message type: server bell
+ RfbConstants.SERVER_BELL,
+
+ // Message type: clipboard text
+ RfbConstants.SERVER_CUT_TEXT,
+ // Padding
+ 0, 0, 0,
+ // Length (test)
+ 0, 0, 0, 4,
+
+ }, new byte[] {
+ // Clipboard text
+ 't', 'e', 's', 't',
+
+ // Message type: frame buffer update
+ RfbConstants.SERVER_FRAMEBUFFER_UPDATE,
+ // Padding
+ 0,
+ // Number of rectangles
+ 0, 3,},
+
+ new byte[] {
+
+ // x, y, width, height: 0x0@4x4
+ 0, 0, 0, 0, 0, 4, 0, 4,
+ // Encoding: desktop size
+ (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 24) & 0xff), (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 16) & 0xff),
+ (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 8) & 0xff), (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 0) & 0xff),},
+
+ new byte[] {
+
+ // x, y, width, height: 0x0@4x4
+ 0, 0, 0, 0, 0, 4, 0, 4,
+ // Encoding: raw rect
+ (byte)((RfbConstants.ENCODING_RAW >> 24) & 0xff), (byte)((RfbConstants.ENCODING_RAW >> 16) & 0xff),
+ (byte)((RfbConstants.ENCODING_RAW >> 8) & 0xff), (byte)((RfbConstants.ENCODING_RAW >> 0) & 0xff),
+ // Raw pixel data 4x4x1 bpp
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,}, new byte[] {11, 12, 13, 14, 15, 16,
+
+ // x, y, width, height: 0x0@2x2
+ 0, 0, 0, 0, 0, 2, 0, 2,
+ // Encoding: copy rect
+ (byte)((RfbConstants.ENCODING_COPY_RECT >> 24) & 0xff), (byte)((RfbConstants.ENCODING_COPY_RECT >> 16) & 0xff),
+ (byte)((RfbConstants.ENCODING_COPY_RECT >> 8) & 0xff), (byte)((RfbConstants.ENCODING_COPY_RECT >> 0) & 0xff),
+ // srcX, srcY: 2x2
+ 0, 2, 0, 2,});
+ }
+ };
+
+ ScreenDescription screen = new ScreenDescription() {
+ {
+ this.bytesPerPixel = 1;
+ }
+ };
+
+ final Element handler = new VncMessageHandler("handler", screen);
+
+ ByteBuffer[] emptyBuf = ByteBuffer.convertByteArraysToByteBuffers(new byte[] {});
+ Element fburSink = new MockSink("fbur", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {}, new byte[] {}));
+ Element bellSink = new MockSink("bell", emptyBuf);
+ Element clipboardSink = new MockSink("clipboard", emptyBuf);
+ Element desktopSizeChangeSink = new MockSink("desktop_size", emptyBuf);
+ Element pixelsSink = new MockSink("pixels",
+ ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,}));
+ Element copyRectSink = new MockSink("copy_rect", emptyBuf);
+
+ Pipeline pipeline = new PipelineImpl("test");
+ pipeline.addAndLink(source, handler);
+ pipeline.add(fburSink, bellSink, clipboardSink, desktopSizeChangeSink, pixelsSink, copyRectSink);
+
+ pipeline.link("handler >" + FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD, "fbur");
+ pipeline.link("handler >" + SERVER_BELL_ADAPTER_PAD, "bell");
+ pipeline.link("handler >" + SERVER_CLIPBOARD_ADAPTER_PAD, "clipboard");
+ pipeline.link("handler >" + PIXEL_ADAPTER_PAD, "pixels");
+
+ pipeline.runMainLoop("source", STDOUT, false, false);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc_3_3_Authentication.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc_3_3_Authentication.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc_3_3_Authentication.java
new file mode 100755
index 0000000..93423da
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc_3_3_Authentication.java
@@ -0,0 +1,291 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package vncclient.vnc;
+
+import java.security.spec.KeySpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.DESKeySpec;
+
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.OneTimeSwitch;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+import streamer.debug.FakeSink;
+import streamer.debug.MockSink;
+import streamer.debug.MockSource;
+
+public class Vnc_3_3_Authentication extends OneTimeSwitch {
+
+ /**
+ * Password to use when authentication is required.
+ */
+ protected String password = null;
+
+ /**
+ * Authentication stage:
+ * <ul>
+ * <li>0 - challenge received, response must be sent
+ * <li>1 - authentication result received.
+ * </ul>
+ */
+ protected int stage = 0;
+
+ public Vnc_3_3_Authentication(String id) {
+ super(id);
+ }
+
+ public Vnc_3_3_Authentication(String id, String password) {
+ super(id);
+ this.password = password;
+ }
+
+ @Override
+ protected void handleOneTimeData(ByteBuffer buf, Link link) {
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+ switch (stage) {
+ case 0: // Read security with optional challenge and response
+ stage0(buf, link);
+
+ break;
+ case 1: // Read authentication response
+ stage1(buf, link);
+ break;
+ }
+
+ }
+
+ /**
+ * Read security type. If connection type is @see
+ * RfbConstants.CONNECTION_FAILED, then throw exception. If connection type is @see
+ * RfbConstants.NO_AUTH, then switch off this element. If connection type is @see
+ * RfbConstants.VNC_AUTH, then read challenge, send encoded password, and read
+ * authentication response.
+ */
+ private void stage0(ByteBuffer buf, Link link) {
+ // At least 4 bytes are necessary
+ if (!cap(buf, 4, UNLIMITED, link, true))
+ return;
+
+ // Read security type
+ int authType = buf.readSignedInt();
+
+ switch (authType) {
+ case RfbConstants.CONNECTION_FAILED: {
+ // Server forbids to connect. Read reason and throw exception
+
+ int length = buf.readSignedInt();
+ String reason = new String(buf.data, buf.offset, length, RfbConstants.US_ASCII_CHARSET);
+
+ throw new RuntimeException("Authentication to VNC server is failed. Reason: " + reason);
+ }
+
+ case RfbConstants.NO_AUTH: {
+ // Client can connect without authorization. Nothing to do.
+ // Switch off this element from circuit
+ switchOff();
+ break;
+ }
+
+ case RfbConstants.VNC_AUTH: {
+ // Read challenge and generate response
+ responseToChallenge(buf, link);
+ break;
+ }
+
+ default:
+ throw new RuntimeException("Unsupported VNC protocol authorization scheme, scheme code: " + authType + ".");
+ }
+
+ }
+
+ private void responseToChallenge(ByteBuffer buf, Link link) {
+ // Challenge is exactly 16 bytes long
+ if (!cap(buf, 16, 16, link, true))
+ return;
+
+ ByteBuffer challenge = buf.slice(buf.cursor, 16, true);
+ buf.unref();
+
+ // Encode challenge with password
+ ByteBuffer response;
+ try {
+ response = encodePassword(challenge, password);
+ challenge.unref();
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot encrypt client password to send to server: " + e.getMessage());
+ }
+
+ if (verbose) {
+ response.putMetadata("sender", this);
+ }
+
+ // Send encoded challenge
+ nextStage();
+ pushDataToOTOut(response);
+
+ }
+
+ private void nextStage() {
+ stage++;
+
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Next stage: " + stage + ".");
+ }
+
+ /**
+ * Encode password using DES encryption with given challenge.
+ *
+ * @param challenge
+ * a random set of bytes.
+ * @param password
+ * a password
+ * @return DES hash of password and challenge
+ */
+ public ByteBuffer encodePassword(ByteBuffer challenge, String password) {
+ if (challenge.length != 16)
+ throw new RuntimeException("Challenge must be exactly 16 bytes long.");
+
+ // VNC password consist of up to eight ASCII characters.
+ byte[] key = {0, 0, 0, 0, 0, 0, 0, 0}; // Padding
+ byte[] passwordAsciiBytes = password.getBytes(RfbConstants.US_ASCII_CHARSET);
+ System.arraycopy(passwordAsciiBytes, 0, key, 0, Math.min(password.length(), 8));
+
+ // Flip bytes (reverse bits) in key
+ for (int i = 0; i < key.length; i++) {
+ key[i] = flipByte(key[i]);
+ }
+
+ try {
+ KeySpec desKeySpec = new DESKeySpec(key);
+ SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
+ SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
+ Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+
+ ByteBuffer buf = new ByteBuffer(cipher.doFinal(challenge.data, challenge.offset, challenge.length));
+
+ return buf;
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot encode password.", e);
+ }
+ }
+
+ /**
+ * Reverse bits in byte, so least significant bit will be most significant
+ * bit. E.g. 01001100 will become 00110010.
+ *
+ * See also: http://www.vidarholen.net/contents/junk/vnc.html ,
+ * http://bytecrafter .blogspot.com/2010/09/des-encryption-as-used-in-vnc.html
+ *
+ * @param b
+ * a byte
+ * @return byte in reverse order
+ */
+ private static byte flipByte(byte b) {
+ int b1_8 = (b & 0x1) << 7;
+ int b2_7 = (b & 0x2) << 5;
+ int b3_6 = (b & 0x4) << 3;
+ int b4_5 = (b & 0x8) << 1;
+ int b5_4 = (b & 0x10) >>> 1;
+ 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;
+ }
+
+ /**
+ * Read authentication result, send nothing.
+ */
+ private void stage1(ByteBuffer buf, Link link) {
+ // Read authentication response
+ if (!cap(buf, 4, 4, link, false))
+ return;
+
+ int authResult = buf.readSignedInt();
+
+ switch (authResult) {
+ case RfbConstants.VNC_AUTH_OK: {
+ // Nothing to do
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Authentication successfull.");
+ break;
+ }
+
+ case RfbConstants.VNC_AUTH_TOO_MANY:
+ throw new RuntimeException("Connection to VNC server failed: too many wrong attempts.");
+
+ case RfbConstants.VNC_AUTH_FAILED:
+ throw new RuntimeException("Connection to VNC server failed: wrong password.");
+
+ default:
+ throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult);
+ }
+
+ switchOff();
+
+ }
+
+ public String toString() {
+ return "VNC3.3 Authentication(" + id + ")";
+ }
+
+ /**
+ * Example.
+ */
+ public static void main(String args[]) {
+ // System.setProperty("streamer.Link.debug", "true");
+ System.setProperty("streamer.Element.debug", "true");
+ // System.setProperty("streamer.Pipeline.debug", "true");
+
+ final String password = "test";
+
+ Element source = new MockSource("source") {
+ {
+ bufs = ByteBuffer.convertByteArraysToByteBuffers(
+ // Request authentication and send 16 byte challenge
+ new byte[] {0, 0, 0, RfbConstants.VNC_AUTH, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
+ // Respond to challenge with AUTH_OK
+ new byte[] {0, 0, 0, RfbConstants.VNC_AUTH_OK});
+ }
+ };
+
+ Element mainSink = new FakeSink("mainSink");
+ final Vnc_3_3_Authentication auth = new Vnc_3_3_Authentication("auth", password);
+ Element initSink = new MockSink("initSink") {
+ {
+ // Expect encoded password
+ bufs = new ByteBuffer[] {auth.encodePassword(new ByteBuffer(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}), password)};
+ }
+ };
+
+ Pipeline pipeline = new PipelineImpl("test");
+ pipeline.addAndLink(source, auth, mainSink);
+ pipeline.add(initSink);
+ pipeline.link("auth >otout", "initSink");
+
+ pipeline.runMainLoop("source", STDOUT, false, false);
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc_3_3_Hello.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc_3_3_Hello.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc_3_3_Hello.java
new file mode 100755
index 0000000..3afff83
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc_3_3_Hello.java
@@ -0,0 +1,115 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package vncclient.vnc;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import streamer.ByteBuffer;
+import streamer.InputStreamSource;
+import streamer.Link;
+import streamer.OneTimeSwitch;
+import streamer.OutputStreamSink;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+/**
+ * VNC server sends hello packet with RFB protocol version, e.g.
+ * "RFB 003.007\n". We need to send response packet with supported protocol
+ * version, e.g. "RFB 003.003\n".
+ */
+public class Vnc_3_3_Hello extends OneTimeSwitch {
+
+ public Vnc_3_3_Hello(String id) {
+ super(id);
+ }
+
+ @Override
+ protected void handleOneTimeData(ByteBuffer buf, Link link) {
+ if (verbose)
+ System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+ // Initial packet is exactly 12 bytes long
+ if (!cap(buf, 12, 12, link, false))
+ return;
+
+ // Read protocol version
+ String rfbProtocol = new String(buf.data, buf.offset, buf.length, RfbConstants.US_ASCII_CHARSET);
+ buf.unref();
+
+ // Server should use RFB protocol 3.x
+ if (!rfbProtocol.contains(RfbConstants.RFB_PROTOCOL_VERSION_MAJOR))
+ throw new RuntimeException("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\".");
+
+ // Send response: we support RFB 3.3 only
+ String ourProtocolString = RfbConstants.RFB_PROTOCOL_VERSION + "\n";
+
+ ByteBuffer outBuf = new ByteBuffer(ourProtocolString.getBytes(RfbConstants.US_ASCII_CHARSET));
+
+ if (verbose) {
+ outBuf.putMetadata("sender", this);
+ outBuf.putMetadata("version", RfbConstants.RFB_PROTOCOL_VERSION);
+ }
+
+ pushDataToOTOut(outBuf);
+
+ // Switch off this element from circuit
+ switchOff();
+
+ }
+
+ public String toString() {
+ return "Vnc3.3 Hello(" + id + ")";
+ }
+
+ /**
+ * Example.
+ */
+ public static void main(String args[]) {
+ // System.setProperty("streamer.Link.debug", "true");
+ System.setProperty("streamer.Element.debug", "true");
+ // System.setProperty("streamer.Pipeline.debug", "true");
+
+ InputStream is = new ByteArrayInputStream("RFB 003.007\ntest".getBytes(RfbConstants.US_ASCII_CHARSET));
+ ByteArrayOutputStream initOS = new ByteArrayOutputStream();
+ ByteArrayOutputStream mainOS = new ByteArrayOutputStream();
+ InputStreamSource inputStreamSource = new InputStreamSource("source", is);
+ OutputStreamSink outputStreamSink = new OutputStreamSink("mainSink", mainOS);
+
+ Vnc_3_3_Hello hello = new Vnc_3_3_Hello("hello");
+
+ Pipeline pipeline = new PipelineImpl("test");
+
+ pipeline.addAndLink(inputStreamSource, hello, outputStreamSink);
+ pipeline.add(new OutputStreamSink("initSink", initOS));
+
+ pipeline.link("hello >" + OneTimeSwitch.OTOUT, "initSink");
+
+ pipeline.runMainLoop("source", STDOUT, false, false);
+
+ String initOut = new String(initOS.toByteArray(), RfbConstants.US_ASCII_CHARSET);
+ String mainOut = new String(mainOS.toByteArray(), RfbConstants.US_ASCII_CHARSET);
+
+ if (!"RFB 003.003\n".equals(initOut))
+ System.err.println("Unexpected value for hello response: \"" + initOut + "\".");
+
+ if (!"test".equals(mainOut))
+ System.err.println("Unexpected value for main data: \"" + mainOut + "\".");
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/resources/jaas_ntlm_config.txt
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/resources/jaas_ntlm_config.txt b/services/console-proxy-rdp/rdpconsole/src/main/resources/jaas_ntlm_config.txt
new file mode 100755
index 0000000..4137609
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/resources/jaas_ntlm_config.txt
@@ -0,0 +1,21 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+MyConfig { com.sun.security.auth.module.Krb5LoginModule required
+ useTicketCache=true
+ doNotPrompt=false;
+};
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt b/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt
old mode 100644
new mode 100755
index dd41683..704f5f5
--- a/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt
+++ b/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt
@@ -30,3 +30,5 @@ fingerprints.
File rdp-key.pem contains private key in PEM format for use with
Wireshark.
+
+As alternative, mimikatz can be used to extract RDP private key.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat b/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat
old mode 100644
new mode 100755
index 14a7bbd..4e19157
--- a/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat
+++ b/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat
@@ -111,6 +111,19 @@ rem Start TS service
net start Termservice
+rem Enable logs
+
+wevtutil sl Microsoft-Windows-TerminalServices-RemoteConnectionManager/Admin /enabled:true /quiet:true
+wevtutil sl Microsoft-Windows-TerminalServices-RemoteConnectionManager/Analytic /enabled:true /quiet:true
+wevtutil sl Microsoft-Windows-TerminalServices-RemoteConnectionManager/Debug /enabled:true /quiet:true
+wevtutil sl Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational /enabled:true /quiet:true
+wevtutil sl Microsoft-Windows-TerminalServices-SessionBroker-Client/Admin /enabled:true /quiet:true
+wevtutil sl Microsoft-Windows-TerminalServices-SessionBroker-Client/Analytic /enabled:true /quiet:true
+wevtutil sl Microsoft-Windows-TerminalServices-SessionBroker-Client/Debug /enabled:true /quiet:true
+wevtutil sl Microsoft-Windows-TerminalServices-SessionBroker-Client/Operational /enabled:true /quiet:true
+wevtutil sl Microsoft-Windows-NTLM/Operational /enabled:true /quiet:true
+
+
rem For Network Monitor Decrypt Expert.