diff --git a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleServerErrorInstructionException.java b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleServerErrorInstructionException.java new file mode 100644 index 000000000..f6889c8e6 --- /dev/null +++ b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleServerErrorInstructionException.java @@ -0,0 +1,55 @@ +/* + * 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 org.apache.guacamole; + +import org.apache.guacamole.protocol.GuacamoleStatus; + +/** + * The exception thrown when the Guacamole server explicitly sends an error + * instruction to the client. The error message and status code reflect the arguments + * of the error instruction as determined by the server. + */ +public class GuacamoleServerErrorInstructionException extends GuacamoleServerException { + /** + * The Guacamole protocol status code, as determined by the server; + */ + private final GuacamoleStatus status; + + /** + * Creates a new GuacamoleServerErrorInstructionException with the given + * message and status. + * + * @param message + * A human readable description of the exception that occurred. + * + * @param status + * The status code, as determined by the server from which the + * instruction originated. + */ + public GuacamoleServerErrorInstructionException(String message, GuacamoleStatus status) { + super(message); + this.status = status; + } + + @Override + public GuacamoleStatus getStatus() { + return status; + } +} diff --git a/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java b/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java index 6cf3d7b3c..55d7c9473 100644 --- a/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java +++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java @@ -21,6 +21,7 @@ package org.apache.guacamole.protocol; import java.util.List; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleServerErrorInstructionException; import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.io.GuacamoleReader; import org.apache.guacamole.io.GuacamoleWriter; @@ -59,12 +60,35 @@ public class ConfiguredGuacamoleSocket extends DelegatingGuacamoleSocket { */ private GuacamoleProtocolVersion protocolVersion = GuacamoleProtocolVersion.VERSION_1_0_0; - + + /** + * Parses the arguments for the Guacamole "error" server instruction and returns + * the corresponding exception. + * @param args The arguments as provided by the server instruction. + * @return An instance of {@link GuacamoleServerErrorInstructionException} configured + * with the server-provided arguments, or a generic {@link GuacamoleServerException} if + * the specified arguments are invalid. + */ + private static GuacamoleServerException parseServerErrorInstructionArgs(List args) { + try { + if (args.size() >= 2) { + int code = Integer.parseInt(args.get(1)); + GuacamoleStatus status = GuacamoleStatus.fromGuacamoleStatusCode(code); + return new GuacamoleServerErrorInstructionException(args.get(0), status); + } + } catch (NumberFormatException ignored) {} + + return new GuacamoleServerException("Invalid error instruction arguments received: " + args); + } + /** * Waits for the instruction having the given opcode, returning that * instruction once it has been read. If the instruction is never read, * an exception is thrown. - * + * + * Respects server control instructions that are allowed during the handshake + * phase, namely {@code error} and {@code disconnect}. + * * @param reader The reader to read instructions from. * @param opcode The opcode of the instruction we are expecting. * @return The instruction having the given opcode. @@ -79,6 +103,12 @@ public class ConfiguredGuacamoleSocket extends DelegatingGuacamoleSocket { if (instruction == null) throw new GuacamoleServerException("End of stream while waiting for \"" + opcode + "\"."); + // Handle server control instructions + if ("disconnect".equals(instruction.getOpcode())) + throw new GuacamoleServerException("Server disconnected while waiting for \"" + opcode + "\"."); + if ("error".equals(instruction.getOpcode())) + throw parseServerErrorInstructionArgs(instruction.getArgs()); + // Ensure instruction has expected opcode if (!instruction.getOpcode().equals(opcode)) throw new GuacamoleServerException("Expected \"" + opcode + "\" instruction but instead received \"" + instruction.getOpcode() + "\".");