// 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 common; import java.awt.Frame; import java.awt.ScrollPane; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.net.InetSocketAddress; import java.util.Arrays; import rdpclient.RdpClient; import streamer.Element; import streamer.Pipeline; import streamer.PipelineImpl; import streamer.SocketWrapper; import streamer.SocketWrapperImpl; import streamer.apr.AprSocketWrapperImpl; import streamer.bco.BcoSocketWrapperImpl; import streamer.ssl.SSLState; import vncclient.VncClient; import common.opt.IntOption; import common.opt.Option; import common.opt.OptionParser; import common.opt.StringEnumerationOption; import common.opt.StringOption; public class Client { enum Protocol { NONE, VNC, RDP, HYPERV } // Common options private final Option help = new Option() { { name = "--help"; alias = "-h"; description = "Show this help text."; } }; private final Option debugLink = new Option() { { name = "--debug-link"; alias = "-DL"; description = "Print debugging messages when packets are trasnferred via links."; } }; private final Option debugElement = new Option() { { name = "--debug-element"; alias = "-DE"; description = "Print debugging messages when packets are received or sent by elements."; } }; private final Option debugPipeline = new Option() { { name = "--debug-pipeline"; alias = "-DP"; description = "Print debugging messages in pipelines."; } }; private final StringOption hostName = new StringOption() { { name = "--host"; alias = "-n"; aliases = new String[] {"--host-name"}; required = true; description = "Name or IP address of host to connect to."; } }; private final IntOption canvasWidth = new IntOption() { { name = "--width"; alias = "-W"; value = 1024; description = "Width of canvas."; } }; private final IntOption canvasHeight = new IntOption() { { name = "--height"; alias = "-H"; value = 768; description = "Height of canvas."; } }; // Protocol specific options private final IntOption vncPort = new IntOption() { { name = "--port"; alias = "-p"; value = 5901; description = "Port of VNC display server to connect to. Calculate as 5900 + display number, e.g. 5900 for display #0, 5901 for display #1, and so on."; } }; private final IntOption rdpPort = new IntOption() { { name = "--port"; alias = "-p"; value = 3389; description = "Port of RDP server to connect to."; } }; private final IntOption hyperVPort = new IntOption() { { name = "--port"; alias = "-p"; value = 2179; description = "Port of HyperV server to connect to."; } }; private final StringOption password = new StringOption() { { name = "--password"; alias = "-P"; required = true; description = "Password to use."; } }; private final StringOption rdpPassword = new StringOption() { { name = "--password"; alias = "-P"; required = false; description = "Password to use. If omitted, then login screen will be shown."; } }; private final StringOption userName = new StringOption() { { name = "--user"; alias = "-U"; value = "Administrator"; description = "User name to use."; } }; private final StringOption domain = new StringOption() { { name = "--domain"; alias = "-D"; value = "Workgroup"; description = "NTLM domain to login into."; } }; private final StringOption hyperVInstanceId = new StringOption() { { name = "--instance"; alias = "-i"; required = true; description = "HyperV instance ID to use."; } }; private final StringEnumerationOption sslImplementation = new StringEnumerationOption() { { name = "--ssl-implementation"; alias = "-j"; value = "apr"; choices = new String[] {"jre", "apr", "bco"}; description = "Select SSL engine to use: JRE standard implementation, Apache Portable Runtime native library, BonuncyCastle.org implementation."; } }; private final Option[] commonOptions = new Option[] {help, debugLink, debugElement, debugPipeline, hostName, canvasWidth, canvasHeight}; private final Option[] vncOptions = new Option[] {vncPort, password}; private final Option[] rdpOptions = new Option[] {sslImplementation, rdpPort, domain, userName, rdpPassword}; private final Option[] hyperVOptions = new Option[] {sslImplementation, hyperVPort, hyperVInstanceId, domain, userName, password}; private static Frame frame; private static SocketWrapper socket; private static ScrollPane scroller; private static ScreenDescription screen; private static BufferedImageCanvas canvas; private InetSocketAddress address; private void help() { System.out.println("Usage: \n java common.Client vnc|rdp|hyperv OPTIONS\n"); System.out.println(Option.toHelp("Common options", commonOptions)); System.out.println(Option.toHelp("VNC options", vncOptions)); System.out.println(Option.toHelp("RDP options", rdpOptions)); System.out.println(Option.toHelp("HyperV options", hyperVOptions)); } public void runClient(String[] args) { try { Protocol protocol = parseOptions(args); if (protocol == Protocol.NONE) return; System.setProperty("streamer.Link.debug", "" + debugLink.used); System.setProperty("streamer.Element.debug", "" + debugElement.used); System.setProperty("streamer.Pipeline.debug", "" + debugPipeline.used); SSLState sslState = new SSLState(); // Create socket wrapper if ("jre".equals(sslImplementation.value)) { socket = new SocketWrapperImpl("socket", sslState); } else if ("apr".equals(sslImplementation.value)) { socket = new AprSocketWrapperImpl("socket", sslState); } else if ("bco".equals(sslImplementation.value)) { socket = new BcoSocketWrapperImpl("socket", sslState); } else { throw new RuntimeException("Unexpected option value: \"" + sslImplementation.value + "\". " + sslImplementation.help()); } screen = new ScreenDescription(); canvas = new BufferedImageCanvas(canvasWidth.value, canvasHeight.value); screen.addSizeChangeListener(new SizeChangeListener() { @Override public void sizeChanged(int width, int height) { if (canvas != null) { canvas.setCanvasSize(width, height); if (scroller != null) scroller.setSize(canvas.getWidth(), canvas.getHeight()); } } }); assemblePipeline(setMainElementAndAddressBasedOnProtocol(protocol, sslState)); frame = createVncClientMainWindow(canvas, protocol.toString() + " " + hostName.value, new WindowAdapter() { @Override public void windowClosing(WindowEvent evt) { shutdown(); } }); try { // Connect socket to remote server and run main loop(s) socket.connect(address); } finally { shutdown(); } } catch (Exception e) { e.printStackTrace(System.err); } } protected static void assemblePipeline(Element main) { Pipeline pipeline = new PipelineImpl("Client"); pipeline.add(socket, main); pipeline.link("socket", main.getId(), "socket"); pipeline.validate(); } private Element setMainElementAndAddressBasedOnProtocol(Protocol protocol, SSLState sslState) { Element main; switch (protocol) { case VNC: address = new InetSocketAddress(hostName.value, vncPort.value); main = new VncClient("client", password.value, screen, canvas); break; case RDP: address = new InetSocketAddress(hostName.value, rdpPort.value); main = new RdpClient("client", hostName.value, domain.value, userName.value, rdpPassword.value, null, screen, canvas, sslState); break; case HYPERV: address = new InetSocketAddress(hostName.value, hyperVPort.value); main = new RdpClient("client", hostName.value, domain.value, userName.value, password.value, hyperVInstanceId.value, screen, canvas, sslState); break; default: address = null; main = null; } return main; } private Protocol parseOptions(String[] args) { String protocolName = (args.length > 0) ? args[0] : ""; Protocol protocol = Protocol.NONE; Option[] options; if (protocolName.equals("vnc")) { protocol = Protocol.VNC; options = join(commonOptions, vncOptions); } else if (protocolName.equals("rdp")) { protocol = Protocol.RDP; options = join(commonOptions, rdpOptions); } else if (protocolName.equals("hyperv")) { protocol = Protocol.HYPERV; options = join(commonOptions, hyperVOptions); } else { help(); return Protocol.NONE; } // Parse all options for given protocol String[] arguments = OptionParser.parseOptions(args, 1, options); if (arguments.length > 0) { System.err.println("[Client] ERROR: Arguments are not allowed here. Check command syntax. Extra arguments: \"" + Arrays.toString(arguments) + "\"."); help(); return Protocol.NONE; } if (help.used) { help(); return Protocol.NONE; } return protocol; } protected static void shutdown() { if (frame != null) { frame.setVisible(false); frame.dispose(); } if (socket != null) socket.shutdown(); } protected static Frame createVncClientMainWindow(BufferedImageCanvas canvas, String title, WindowListener windowListener) { // Create AWT windows Frame frame = new Frame(title + " - RDP"); // Use scrolling pane to support screens, which are larger than ours scroller = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); scroller.add(canvas); scroller.setSize(canvas.getWidth(), canvas.getHeight()); frame.add(scroller); frame.pack(); frame.setVisible(true); frame.addWindowListener(windowListener); return frame; } /** * Join two arrays with options and return new array. */ private Option[] join(Option[] a1, Option[] a2) { // Extend first array Option[] result = Arrays.copyOf(a1, a1.length + a2.length); // Append second array to first for (int i = 0, p = a1.length; i < a2.length; i++, p++) result[p] = a2[i]; return result; } public static void main(String args[]) { // *DEBUG*/System.setProperty("javax.net.debug", "ssl"); // * DEBUG */System.setProperty("javax.net.debug", "ssl:record:packet"); new Client().runClient(args); } }