| // 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 transferred 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); | |
| } | |
| } |