forked from shaba/openuds
Backported guacamole for 2.0 availability
This commit is contained in:
parent
0d28a5b5f7
commit
2eef7222d5
5
guacamole-tunnel/NOTICE
Normal file
5
guacamole-tunnel/NOTICE
Normal file
@ -0,0 +1,5 @@
|
||||
Apache Guacamole
|
||||
Copyright 2016 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.tunnel;
|
||||
|
||||
import com.google.inject.Module;
|
||||
|
||||
/**
|
||||
* Generic means of loading a tunnel without adding explicit dependencies within
|
||||
* the main ServletModule, as not all servlet containers may have the classes
|
||||
* required by all tunnel implementations.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public interface TunnelLoader extends Module {
|
||||
|
||||
/**
|
||||
* Checks whether this type of tunnel is supported by the servlet container.
|
||||
*
|
||||
* @return true if this type of tunnel is supported and can be loaded
|
||||
* without errors, false otherwise.
|
||||
*/
|
||||
public boolean isSupported();
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* This file has been modified from the original, upstream version to facilitate
|
||||
* integration with OpenUDS.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.tunnel;
|
||||
|
||||
import org.apache.guacamole.tunnel.http.RestrictedGuacamoleHTTPTunnelServlet;
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Module which loads tunnel implementations.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class TunnelModule extends ServletModule {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(TunnelModule.class);
|
||||
|
||||
/**
|
||||
* Classnames of all implementation-specific WebSocket tunnel modules.
|
||||
*/
|
||||
private static final String[] WEBSOCKET_MODULES = {
|
||||
"org.apache.guacamole.tunnel.websocket.WebSocketTunnelModule",
|
||||
"org.apache.guacamole.tunnel.websocket.jetty8.WebSocketTunnelModule",
|
||||
"org.apache.guacamole.tunnel.websocket.jetty9.WebSocketTunnelModule",
|
||||
"org.apache.guacamole.tunnel.websocket.tomcat.WebSocketTunnelModule"
|
||||
};
|
||||
|
||||
private boolean loadWebSocketModule(String classname) {
|
||||
|
||||
try {
|
||||
|
||||
// Attempt to find WebSocket module
|
||||
Class<?> module = Class.forName(classname);
|
||||
|
||||
// Create loader
|
||||
TunnelLoader loader = (TunnelLoader) module.getConstructor().newInstance();
|
||||
|
||||
// Install module, if supported
|
||||
if (loader.isSupported()) {
|
||||
install(loader);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If no such class or constructor, etc., then this particular
|
||||
// WebSocket support is not present
|
||||
catch (ClassNotFoundException e) {}
|
||||
catch (NoClassDefFoundError e) {}
|
||||
catch (NoSuchMethodException e) {}
|
||||
|
||||
// Log errors which indicate bugs
|
||||
catch (InstantiationException e) {
|
||||
logger.debug("Error instantiating WebSocket module.", e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
logger.debug("Error instantiating WebSocket module.", e);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
logger.debug("Error instantiating WebSocket module.", e);
|
||||
}
|
||||
|
||||
// Load attempt failed
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureServlets() {
|
||||
|
||||
bind(TunnelRequestService.class);
|
||||
|
||||
// Set up HTTP tunnel
|
||||
serve("/tunnel").with(RestrictedGuacamoleHTTPTunnelServlet.class);
|
||||
|
||||
// Try to load each WebSocket tunnel in sequence
|
||||
for (String classname : WEBSOCKET_MODULES) {
|
||||
if (loadWebSocketModule(classname)) {
|
||||
logger.debug("WebSocket module loaded: {}", classname);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Warn of lack of WebSocket
|
||||
logger.info("WebSocket support NOT present. Only HTTP will be used.");
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,371 @@
|
||||
/*
|
||||
* 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.tunnel;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.guacamole.GuacamoleClientException;
|
||||
import org.apache.guacamole.GuacamoleClientException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
|
||||
/**
|
||||
* A request object which provides only the functions absolutely required to
|
||||
* retrieve and connect to a tunnel.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public abstract class TunnelRequest {
|
||||
|
||||
/**
|
||||
* The name of the request parameter containing the user's authentication
|
||||
* token.
|
||||
*/
|
||||
public static final String AUTH_TOKEN_PARAMETER = "token";
|
||||
|
||||
/**
|
||||
* The name of the parameter containing the identifier of the
|
||||
* AuthenticationProvider associated with the UserContext containing the
|
||||
* object to which a tunnel is being requested.
|
||||
*/
|
||||
public static final String AUTH_PROVIDER_IDENTIFIER_PARAMETER = "GUAC_DATA_SOURCE";
|
||||
|
||||
/**
|
||||
* The name of the parameter specifying the type of object to which a
|
||||
* tunnel is being requested. Currently, this may be "c" for a Guacamole
|
||||
* connection, or "g" for a Guacamole connection group.
|
||||
*/
|
||||
public static final String TYPE_PARAMETER = "GUAC_TYPE";
|
||||
|
||||
/**
|
||||
* The name of the parameter containing the unique identifier of the object
|
||||
* to which a tunnel is being requested.
|
||||
*/
|
||||
public static final String IDENTIFIER_PARAMETER = "GUAC_ID";
|
||||
|
||||
/**
|
||||
* The name of the parameter containing the desired display width, in
|
||||
* pixels.
|
||||
*/
|
||||
public static final String WIDTH_PARAMETER = "GUAC_WIDTH";
|
||||
|
||||
/**
|
||||
* The name of the parameter containing the desired display height, in
|
||||
* pixels.
|
||||
*/
|
||||
public static final String HEIGHT_PARAMETER = "GUAC_HEIGHT";
|
||||
|
||||
/**
|
||||
* The name of the parameter containing the desired display resolution, in
|
||||
* DPI.
|
||||
*/
|
||||
public static final String DPI_PARAMETER = "GUAC_DPI";
|
||||
|
||||
/**
|
||||
* The name of the parameter specifying one supported audio mimetype. This
|
||||
* will normally appear multiple times within a single tunnel request -
|
||||
* once for each mimetype.
|
||||
*/
|
||||
public static final String AUDIO_PARAMETER = "GUAC_AUDIO";
|
||||
|
||||
/**
|
||||
* The name of the parameter specifying one supported video mimetype. This
|
||||
* will normally appear multiple times within a single tunnel request -
|
||||
* once for each mimetype.
|
||||
*/
|
||||
public static final String VIDEO_PARAMETER = "GUAC_VIDEO";
|
||||
|
||||
/**
|
||||
* The name of the parameter specifying one supported image mimetype. This
|
||||
* will normally appear multiple times within a single tunnel request -
|
||||
* once for each mimetype.
|
||||
*/
|
||||
public static final String IMAGE_PARAMETER = "GUAC_IMAGE";
|
||||
|
||||
/**
|
||||
* All supported object types that can be used as the destination of a
|
||||
* tunnel.
|
||||
*/
|
||||
public static enum Type {
|
||||
|
||||
/**
|
||||
* A Guacamole connection.
|
||||
*/
|
||||
CONNECTION("c"),
|
||||
|
||||
/**
|
||||
* A Guacamole connection group.
|
||||
*/
|
||||
CONNECTION_GROUP("g");
|
||||
|
||||
/**
|
||||
* The parameter value which denotes a destination object of this type.
|
||||
*/
|
||||
final String PARAMETER_VALUE;
|
||||
|
||||
/**
|
||||
* Defines a Type having the given corresponding parameter value.
|
||||
*
|
||||
* @param value
|
||||
* The parameter value which denotes a destination object of this
|
||||
* type.
|
||||
*/
|
||||
Type(String value) {
|
||||
PARAMETER_VALUE = value;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the value of the parameter having the given name.
|
||||
*
|
||||
* @param name
|
||||
* The name of the parameter to return.
|
||||
*
|
||||
* @return
|
||||
* The value of the parameter having the given name, or null if no such
|
||||
* parameter was specified.
|
||||
*/
|
||||
public abstract String getParameter(String name);
|
||||
|
||||
/**
|
||||
* Returns a list of all values specified for the given parameter.
|
||||
*
|
||||
* @param name
|
||||
* The name of the parameter to return.
|
||||
*
|
||||
* @return
|
||||
* All values of the parameter having the given name , or null if no
|
||||
* such parameter was specified.
|
||||
*/
|
||||
public abstract List<String> getParameterValues(String name);
|
||||
|
||||
/**
|
||||
* Returns the value of the parameter having the given name, throwing an
|
||||
* exception if the parameter is missing.
|
||||
*
|
||||
* @param name
|
||||
* The name of the parameter to return.
|
||||
*
|
||||
* @return
|
||||
* The value of the parameter having the given name.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the parameter is not present in the request.
|
||||
*/
|
||||
public String getRequiredParameter(String name) throws GuacamoleException {
|
||||
|
||||
// Pull requested parameter, aborting if absent
|
||||
String value = getParameter(name);
|
||||
if (value == null)
|
||||
throw new GuacamoleClientException("Parameter \"" + name + "\" is required.");
|
||||
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the integer value of the parameter having the given name,
|
||||
* throwing an exception if the parameter cannot be parsed.
|
||||
*
|
||||
* @param name
|
||||
* The name of the parameter to return.
|
||||
*
|
||||
* @return
|
||||
* The integer value of the parameter having the given name, or null if
|
||||
* the parameter is missing.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the parameter is not a valid integer.
|
||||
*/
|
||||
public Integer getIntegerParameter(String name) throws GuacamoleException {
|
||||
|
||||
// Pull requested parameter
|
||||
String value = getParameter(name);
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
// Attempt to parse as an integer
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
// Rethrow any parsing error as a GuacamoleClientException
|
||||
catch (NumberFormatException e) {
|
||||
throw new GuacamoleClientException("Parameter \"" + name + "\" must be a valid integer.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication token associated with this tunnel request.
|
||||
*
|
||||
* @return
|
||||
* The authentication token associated with this tunnel request, or
|
||||
* null if no authentication token is present.
|
||||
*/
|
||||
public String getAuthenticationToken() {
|
||||
return getParameter(AUTH_TOKEN_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier of the AuthenticationProvider associated with the
|
||||
* UserContext from which the connection or connection group is to be
|
||||
* retrieved when the tunnel is created. In the context of the REST API and
|
||||
* the JavaScript side of the web application, this is referred to as the
|
||||
* data source identifier.
|
||||
*
|
||||
* @return
|
||||
* The identifier of the AuthenticationProvider associated with the
|
||||
* UserContext from which the connection or connection group is to be
|
||||
* retrieved when the tunnel is created.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the identifier was not present in the request.
|
||||
*/
|
||||
public String getAuthenticationProviderIdentifier()
|
||||
throws GuacamoleException {
|
||||
return getRequiredParameter(AUTH_PROVIDER_IDENTIFIER_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of object for which the tunnel is being requested.
|
||||
*
|
||||
* @return
|
||||
* The type of object for which the tunnel is being requested.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the type was not present in the request, or if the type requested
|
||||
* is in the wrong format.
|
||||
*/
|
||||
public Type getType() throws GuacamoleException {
|
||||
|
||||
String type = getRequiredParameter(TYPE_PARAMETER);
|
||||
|
||||
// For each possible object type
|
||||
for (Type possibleType : Type.values()) {
|
||||
|
||||
// Match against defined parameter value
|
||||
if (type.equals(possibleType.PARAMETER_VALUE))
|
||||
return possibleType;
|
||||
|
||||
}
|
||||
|
||||
throw new GuacamoleClientException("Illegal identifier - unknown type.");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier of the destination of the tunnel being requested.
|
||||
* As there are multiple types of destination objects available, and within
|
||||
* multiple data sources, the associated object type and data source are
|
||||
* also necessary to determine what this identifier refers to.
|
||||
*
|
||||
* @return
|
||||
* The identifier of the destination of the tunnel being requested.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the identifier was not present in the request.
|
||||
*/
|
||||
public String getIdentifier() throws GuacamoleException {
|
||||
return getRequiredParameter(IDENTIFIER_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display width desired for the Guacamole session over the
|
||||
* tunnel being requested.
|
||||
*
|
||||
* @return
|
||||
* The display width desired for the Guacamole session over the tunnel
|
||||
* being requested, or null if no width was given.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the width specified was not a valid integer.
|
||||
*/
|
||||
public Integer getWidth() throws GuacamoleException {
|
||||
return getIntegerParameter(WIDTH_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display height desired for the Guacamole session over the
|
||||
* tunnel being requested.
|
||||
*
|
||||
* @return
|
||||
* The display height desired for the Guacamole session over the tunnel
|
||||
* being requested, or null if no width was given.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the height specified was not a valid integer.
|
||||
*/
|
||||
public Integer getHeight() throws GuacamoleException {
|
||||
return getIntegerParameter(HEIGHT_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display resolution desired for the Guacamole session over
|
||||
* the tunnel being requested, in DPI.
|
||||
*
|
||||
* @return
|
||||
* The display resolution desired for the Guacamole session over the
|
||||
* tunnel being requested, or null if no resolution was given.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the resolution specified was not a valid integer.
|
||||
*/
|
||||
public Integer getDPI() throws GuacamoleException {
|
||||
return getIntegerParameter(DPI_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all audio mimetypes declared as supported within the
|
||||
* tunnel request.
|
||||
*
|
||||
* @return
|
||||
* A list of all audio mimetypes declared as supported within the
|
||||
* tunnel request, or null if no mimetypes were specified.
|
||||
*/
|
||||
public List<String> getAudioMimetypes() {
|
||||
return getParameterValues(AUDIO_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all video mimetypes declared as supported within the
|
||||
* tunnel request.
|
||||
*
|
||||
* @return
|
||||
* A list of all video mimetypes declared as supported within the
|
||||
* tunnel request, or null if no mimetypes were specified.
|
||||
*/
|
||||
public List<String> getVideoMimetypes() {
|
||||
return getParameterValues(VIDEO_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all image mimetypes declared as supported within the
|
||||
* tunnel request.
|
||||
*
|
||||
* @return
|
||||
* A list of all image mimetypes declared as supported within the
|
||||
* tunnel request, or null if no mimetypes were specified.
|
||||
*/
|
||||
public List<String> getImageMimetypes() {
|
||||
return getParameterValues(IMAGE_PARAMETER);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* This file has been modified from the original, upstream version to facilitate
|
||||
* integration with OpenUDS.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.tunnel;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.util.List;
|
||||
import org.apache.guacamole.GuacamoleClientException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.net.GuacamoleSocket;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.guacamole.net.InetGuacamoleSocket;
|
||||
import org.apache.guacamole.net.SimpleGuacamoleTunnel;
|
||||
import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket;
|
||||
import org.apache.guacamole.protocol.GuacamoleClientInformation;
|
||||
import org.apache.guacamole.protocol.GuacamoleConfiguration;
|
||||
import org.openuds.guacamole.connection.ConnectionService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Utility class that takes a standard request from the Guacamole JavaScript
|
||||
* client and produces the corresponding GuacamoleTunnel. The implementation
|
||||
* of this utility is specific to the form of request used by the upstream
|
||||
* Guacamole web application, and is not necessarily useful to applications
|
||||
* that use purely the Guacamole API.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
* @author Vasily Loginov
|
||||
*/
|
||||
@Singleton
|
||||
public class TunnelRequestService {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(TunnelRequestService.class);
|
||||
|
||||
/**
|
||||
* Service for retrieving remotely-maintained connection information.
|
||||
*/
|
||||
@Inject
|
||||
private ConnectionService connectionService;
|
||||
|
||||
/**
|
||||
* The hostname of the server hosting guacd.
|
||||
*/
|
||||
private static final String GUACD_HOSTNAME = "127.0.0.1";
|
||||
|
||||
/**
|
||||
* The port that guacd will be listening on.
|
||||
*/
|
||||
private static final int GUACD_PORT = 4822;
|
||||
|
||||
/**
|
||||
* Creates a new tunnel using the parameters and credentials present in
|
||||
* the given request.
|
||||
*
|
||||
* @param request
|
||||
* The HttpServletRequest describing the tunnel to create.
|
||||
*
|
||||
* @return
|
||||
* The created tunnel, or null if the tunnel could not be created.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while creating the tunnel.
|
||||
*/
|
||||
public GuacamoleTunnel createTunnel(TunnelRequest request) throws GuacamoleException {
|
||||
|
||||
// Pull OpenUDS-specific "data" parameter
|
||||
String data = request.getParameter("data");
|
||||
if (data == null || data.isEmpty()) {
|
||||
logger.debug("No ID received in tunnel connect request.");
|
||||
throw new GuacamoleClientException("Connection data not provided.");
|
||||
}
|
||||
|
||||
logger.debug("Establishing tunnel and connection with data from \"{}\"...", data);
|
||||
|
||||
// Get connection from remote service
|
||||
GuacamoleConfiguration config = connectionService.getConnectionConfiguration(data);
|
||||
if (config == null)
|
||||
throw new GuacamoleClientException("Connection configuration could not be retrieved.");
|
||||
|
||||
// Get client information
|
||||
GuacamoleClientInformation info = new GuacamoleClientInformation();
|
||||
|
||||
// Set width if provided
|
||||
String width = request.getParameter("GUAC_WIDTH");
|
||||
if (width != null)
|
||||
info.setOptimalScreenWidth(Integer.parseInt(width));
|
||||
|
||||
// Set height if provided
|
||||
String height = request.getParameter("GUAC_HEIGHT");
|
||||
if (height != null)
|
||||
info.setOptimalScreenHeight(Integer.parseInt(height));
|
||||
|
||||
// Set resolution if provided
|
||||
String dpi = request.getParameter("GUAC_DPI");
|
||||
if (dpi != null)
|
||||
info.setOptimalResolution(Integer.parseInt(dpi));
|
||||
|
||||
// Add audio mimetypes
|
||||
List<String> audio_mimetypes = request.getParameterValues("GUAC_AUDIO");
|
||||
if (audio_mimetypes != null)
|
||||
info.getAudioMimetypes().addAll(audio_mimetypes);
|
||||
|
||||
// Add video mimetypes
|
||||
List<String> video_mimetypes = request.getParameterValues("GUAC_VIDEO");
|
||||
if (video_mimetypes != null)
|
||||
info.getVideoMimetypes().addAll(video_mimetypes);
|
||||
|
||||
// Add image mimetypes
|
||||
List<String> image_mimetypes = request.getParameterValues("GUAC_IMAGE");
|
||||
if (image_mimetypes != null)
|
||||
info.getImageMimetypes().addAll(image_mimetypes);
|
||||
|
||||
// Connect socket for connection
|
||||
GuacamoleSocket socket;
|
||||
try {
|
||||
socket = new ConfiguredGuacamoleSocket(
|
||||
new InetGuacamoleSocket(GUACD_HOSTNAME, GUACD_PORT),
|
||||
config, info
|
||||
);
|
||||
}
|
||||
|
||||
// Log any errors during connection
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("Unable to connect to guacd.", e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Return corresponding tunnel
|
||||
return new SimpleGuacamoleTunnel(socket);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.tunnel.http;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.guacamole.tunnel.TunnelRequest;
|
||||
|
||||
/**
|
||||
* HTTP-specific implementation of TunnelRequest.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class HTTPTunnelRequest extends TunnelRequest {
|
||||
|
||||
/**
|
||||
* A copy of the parameters obtained from the HttpServletRequest used to
|
||||
* construct the HTTPTunnelRequest.
|
||||
*/
|
||||
private final Map<String, List<String>> parameterMap =
|
||||
new HashMap<String, List<String>>();
|
||||
|
||||
/**
|
||||
* Creates a HTTPTunnelRequest which copies and exposes the parameters
|
||||
* from the given HttpServletRequest.
|
||||
*
|
||||
* @param request
|
||||
* The HttpServletRequest to copy parameter values from.
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // getParameterMap() is defined as returning Map<String, String[]>
|
||||
public HTTPTunnelRequest(HttpServletRequest request) {
|
||||
|
||||
// For each parameter
|
||||
for (Map.Entry<String, String[]> mapEntry : ((Map<String, String[]>)
|
||||
request.getParameterMap()).entrySet()) {
|
||||
|
||||
// Get parameter name and corresponding values
|
||||
String parameterName = mapEntry.getKey();
|
||||
List<String> parameterValues = Arrays.asList(mapEntry.getValue());
|
||||
|
||||
// Store copy of all values in our own map
|
||||
parameterMap.put(
|
||||
parameterName,
|
||||
new ArrayList<String>(parameterValues)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParameter(String name) {
|
||||
List<String> values = getParameterValues(name);
|
||||
|
||||
// Return the first value from the list if available
|
||||
if (values != null && !values.isEmpty())
|
||||
return values.get(0);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getParameterValues(String name) {
|
||||
return parameterMap.get(name);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file has been modified from the original, upstream version to facilitate
|
||||
* integration with OpenUDS.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.tunnel.http;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.guacamole.servlet.GuacamoleHTTPTunnelServlet;
|
||||
import org.apache.guacamole.tunnel.TunnelRequestService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Connects users to a tunnel associated with the authorized connection
|
||||
* having the given ID.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
@Singleton
|
||||
public class RestrictedGuacamoleHTTPTunnelServlet extends GuacamoleHTTPTunnelServlet {
|
||||
|
||||
/**
|
||||
* Service for handling tunnel requests.
|
||||
*/
|
||||
@Inject
|
||||
private TunnelRequestService tunnelRequestService;
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(RestrictedGuacamoleHTTPTunnelServlet.class);
|
||||
|
||||
@Override
|
||||
protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException {
|
||||
|
||||
// Attempt to create HTTP tunnel
|
||||
GuacamoleTunnel tunnel = tunnelRequestService.createTunnel(new HTTPTunnelRequest(request));
|
||||
|
||||
// If successful, warn of lack of WebSocket
|
||||
logger.info("Using HTTP tunnel (not WebSocket). Performance may be sub-optimal.");
|
||||
|
||||
return tunnel;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes which leverage Guacamole's built-in HTTP tunnel implementation.
|
||||
*/
|
||||
package org.apache.guacamole.tunnel.http;
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes which are common to all tunnel implementations.
|
||||
*/
|
||||
package org.apache.guacamole.tunnel;
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* This file has been modified from the original, upstream version to facilitate
|
||||
* integration with OpenUDS.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.tunnel.websocket;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
import java.util.Map;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.HandshakeResponse;
|
||||
import javax.websocket.Session;
|
||||
import javax.websocket.server.HandshakeRequest;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.guacamole.tunnel.TunnelRequest;
|
||||
import org.apache.guacamole.websocket.GuacamoleWebSocketTunnelEndpoint;
|
||||
import org.apache.guacamole.tunnel.TunnelRequestService;
|
||||
|
||||
/**
|
||||
* Tunnel implementation which uses WebSocket as a tunnel backend, rather than
|
||||
* HTTP, properly parsing connection IDs included in the connection request.
|
||||
*/
|
||||
public class RestrictedGuacamoleWebSocketTunnelEndpoint extends GuacamoleWebSocketTunnelEndpoint {
|
||||
|
||||
/**
|
||||
* Unique string which shall be used to store the TunnelRequest
|
||||
* associated with a WebSocket connection.
|
||||
*/
|
||||
private static final String TUNNEL_REQUEST_PROPERTY = "WS_GUAC_TUNNEL_REQUEST";
|
||||
|
||||
/**
|
||||
* Unique string which shall be used to store the TunnelRequestService to
|
||||
* be used for processing TunnelRequests.
|
||||
*/
|
||||
private static final String TUNNEL_REQUEST_SERVICE_PROPERTY = "WS_GUAC_TUNNEL_REQUEST_SERVICE";
|
||||
|
||||
/**
|
||||
* Configurator implementation which stores the requested GuacamoleTunnel
|
||||
* within the user properties. The GuacamoleTunnel will be later retrieved
|
||||
* during the connection process.
|
||||
*/
|
||||
public static class Configurator extends ServerEndpointConfig.Configurator {
|
||||
|
||||
/**
|
||||
* Provider which provides instances of a service for handling
|
||||
* tunnel requests.
|
||||
*/
|
||||
private final Provider<TunnelRequestService> tunnelRequestServiceProvider;
|
||||
|
||||
/**
|
||||
* Creates a new Configurator which uses the given tunnel request
|
||||
* service provider to retrieve the necessary service to handle new
|
||||
* connections requests.
|
||||
*
|
||||
* @param tunnelRequestServiceProvider
|
||||
* The tunnel request service provider to use for all new
|
||||
* connections.
|
||||
*/
|
||||
public Configurator(Provider<TunnelRequestService> tunnelRequestServiceProvider) {
|
||||
this.tunnelRequestServiceProvider = tunnelRequestServiceProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyHandshake(ServerEndpointConfig config,
|
||||
HandshakeRequest request, HandshakeResponse response) {
|
||||
|
||||
super.modifyHandshake(config, request, response);
|
||||
|
||||
// Store tunnel request and tunnel request service for retrieval
|
||||
// upon WebSocket open
|
||||
Map<String, Object> userProperties = config.getUserProperties();
|
||||
userProperties.clear();
|
||||
userProperties.put(TUNNEL_REQUEST_PROPERTY, new WebSocketTunnelRequest(request));
|
||||
userProperties.put(TUNNEL_REQUEST_SERVICE_PROPERTY, tunnelRequestServiceProvider.get());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuacamoleTunnel createTunnel(Session session,
|
||||
EndpointConfig config) throws GuacamoleException {
|
||||
|
||||
Map<String, Object> userProperties = config.getUserProperties();
|
||||
|
||||
// Get original tunnel request
|
||||
TunnelRequest tunnelRequest = (TunnelRequest) userProperties.get(TUNNEL_REQUEST_PROPERTY);
|
||||
if (tunnelRequest == null)
|
||||
return null;
|
||||
|
||||
// Get tunnel request service
|
||||
TunnelRequestService tunnelRequestService = (TunnelRequestService) userProperties.get(TUNNEL_REQUEST_SERVICE_PROPERTY);
|
||||
if (tunnelRequestService == null)
|
||||
return null;
|
||||
|
||||
// Create and return tunnel
|
||||
return tunnelRequestService.createTunnel(tunnelRequest);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* This file has been modified from the original, upstream version to facilitate
|
||||
* integration with OpenUDS.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.tunnel.websocket;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import java.util.Arrays;
|
||||
import javax.websocket.DeploymentException;
|
||||
import javax.websocket.server.ServerContainer;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
import org.apache.guacamole.tunnel.TunnelLoader;
|
||||
import org.apache.guacamole.tunnel.TunnelRequestService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Loads the JSR-356 WebSocket tunnel implementation.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class WebSocketTunnelModule extends ServletModule implements TunnelLoader {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(WebSocketTunnelModule.class);
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
|
||||
try {
|
||||
|
||||
// Attempt to find WebSocket servlet
|
||||
Class.forName("javax.websocket.Endpoint");
|
||||
|
||||
// Support found
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// If no such servlet class, this particular WebSocket support
|
||||
// is not present
|
||||
catch (ClassNotFoundException e) {}
|
||||
catch (NoClassDefFoundError e) {}
|
||||
|
||||
// Support not found
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureServlets() {
|
||||
|
||||
logger.info("Loading JSR-356 WebSocket support...");
|
||||
|
||||
// Get container
|
||||
ServerContainer container = (ServerContainer) getServletContext().getAttribute("javax.websocket.server.ServerContainer");
|
||||
if (container == null) {
|
||||
logger.warn("ServerContainer attribute required by JSR-356 is missing. Cannot load JSR-356 WebSocket support.");
|
||||
return;
|
||||
}
|
||||
|
||||
Provider<TunnelRequestService> tunnelRequestServiceProvider = getProvider(TunnelRequestService.class);
|
||||
|
||||
// Build configuration for WebSocket tunnel
|
||||
ServerEndpointConfig config =
|
||||
ServerEndpointConfig.Builder.create(RestrictedGuacamoleWebSocketTunnelEndpoint.class, "/websocket-tunnel")
|
||||
.configurator(new RestrictedGuacamoleWebSocketTunnelEndpoint.Configurator(tunnelRequestServiceProvider))
|
||||
.subprotocols(Arrays.asList(new String[]{"guacamole"}))
|
||||
.build();
|
||||
|
||||
try {
|
||||
|
||||
// Add configuration to container
|
||||
container.addEndpoint(config);
|
||||
|
||||
}
|
||||
catch (DeploymentException e) {
|
||||
logger.error("Unable to deploy WebSocket tunnel.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.tunnel.websocket;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.websocket.server.HandshakeRequest;
|
||||
import org.apache.guacamole.tunnel.TunnelRequest;
|
||||
|
||||
/**
|
||||
* WebSocket-specific implementation of TunnelRequest.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class WebSocketTunnelRequest extends TunnelRequest {
|
||||
|
||||
/**
|
||||
* All parameters passed via HTTP to the WebSocket handshake.
|
||||
*/
|
||||
private final Map<String, List<String>> handshakeParameters;
|
||||
|
||||
/**
|
||||
* Creates a TunnelRequest implementation which delegates parameter and
|
||||
* session retrieval to the given HandshakeRequest.
|
||||
*
|
||||
* @param request The HandshakeRequest to wrap.
|
||||
*/
|
||||
public WebSocketTunnelRequest(HandshakeRequest request) {
|
||||
this.handshakeParameters = request.getParameterMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParameter(String name) {
|
||||
|
||||
// Pull list of values, if present
|
||||
List<String> values = getParameterValues(name);
|
||||
if (values == null || values.isEmpty())
|
||||
return null;
|
||||
|
||||
// Return first parameter value arbitrarily
|
||||
return values.get(0);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getParameterValues(String name) {
|
||||
return handshakeParameters.get(name);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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.tunnel.websocket.jetty8;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.eclipse.jetty.websocket.WebSocket;
|
||||
import org.eclipse.jetty.websocket.WebSocket.Connection;
|
||||
import org.eclipse.jetty.websocket.WebSocketServlet;
|
||||
import org.apache.guacamole.GuacamoleClientException;
|
||||
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
||||
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
||||
import org.apache.guacamole.tunnel.http.HTTPTunnelRequest;
|
||||
import org.apache.guacamole.tunnel.TunnelRequest;
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A WebSocketServlet partial re-implementation of GuacamoleTunnelServlet.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelServlet.class);
|
||||
|
||||
/**
|
||||
* The default, minimum buffer size for instructions.
|
||||
*/
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
/**
|
||||
* Sends the given status on the given WebSocket connection and closes the
|
||||
* connection.
|
||||
*
|
||||
* @param connection The WebSocket connection to close.
|
||||
* @param guac_status The status to send.
|
||||
*/
|
||||
public static void closeConnection(Connection connection,
|
||||
GuacamoleStatus guac_status) {
|
||||
|
||||
connection.close(guac_status.getWebSocketCode(),
|
||||
Integer.toString(guac_status.getGuacamoleStatusCode()));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
|
||||
|
||||
final TunnelRequest tunnelRequest = new HTTPTunnelRequest(request);
|
||||
|
||||
// Return new WebSocket which communicates through tunnel
|
||||
return new WebSocket.OnTextMessage() {
|
||||
|
||||
/**
|
||||
* The GuacamoleTunnel associated with the connected WebSocket. If
|
||||
* the WebSocket has not yet been connected, this will be null.
|
||||
*/
|
||||
private GuacamoleTunnel tunnel = null;
|
||||
|
||||
@Override
|
||||
public void onMessage(String string) {
|
||||
|
||||
// Ignore inbound messages if there is no associated tunnel
|
||||
if (tunnel == null)
|
||||
return;
|
||||
|
||||
GuacamoleWriter writer = tunnel.acquireWriter();
|
||||
|
||||
// Write message received
|
||||
try {
|
||||
writer.write(string.toCharArray());
|
||||
}
|
||||
catch (GuacamoleConnectionClosedException e) {
|
||||
logger.debug("Connection to guacd closed.", e);
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.debug("WebSocket tunnel write failed.", e);
|
||||
}
|
||||
|
||||
tunnel.releaseWriter();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(final Connection connection) {
|
||||
|
||||
try {
|
||||
tunnel = doConnect(tunnelRequest);
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
|
||||
logger.debug("Error connecting WebSocket tunnel.", e);
|
||||
closeConnection(connection, e.getStatus());
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not start connection if tunnel does not exist
|
||||
if (tunnel == null) {
|
||||
closeConnection(connection, GuacamoleStatus.RESOURCE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
Thread readThread = new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
StringBuilder buffer = new StringBuilder(BUFFER_SIZE);
|
||||
GuacamoleReader reader = tunnel.acquireReader();
|
||||
char[] readMessage;
|
||||
|
||||
try {
|
||||
|
||||
// Send tunnel UUID
|
||||
connection.sendMessage(new GuacamoleInstruction(
|
||||
GuacamoleTunnel.INTERNAL_DATA_OPCODE,
|
||||
tunnel.getUUID().toString()
|
||||
).toString());
|
||||
|
||||
try {
|
||||
|
||||
// Attempt to read
|
||||
while ((readMessage = reader.read()) != null) {
|
||||
|
||||
// Buffer message
|
||||
buffer.append(readMessage);
|
||||
|
||||
// Flush if we expect to wait or buffer is getting full
|
||||
if (!reader.available() || buffer.length() >= BUFFER_SIZE) {
|
||||
connection.sendMessage(buffer.toString());
|
||||
buffer.setLength(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// No more data
|
||||
closeConnection(connection, GuacamoleStatus.SUCCESS);
|
||||
|
||||
}
|
||||
|
||||
// Catch any thrown guacamole exception and attempt
|
||||
// to pass within the WebSocket connection, logging
|
||||
// each error appropriately.
|
||||
catch (GuacamoleClientException e) {
|
||||
logger.info("WebSocket connection terminated: {}", e.getMessage());
|
||||
logger.debug("WebSocket connection terminated due to client error.", e);
|
||||
closeConnection(connection, e.getStatus());
|
||||
}
|
||||
catch (GuacamoleConnectionClosedException e) {
|
||||
logger.debug("Connection to guacd closed.", e);
|
||||
closeConnection(connection, GuacamoleStatus.SUCCESS);
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
|
||||
logger.debug("Internal error during connection to guacd.", e);
|
||||
closeConnection(connection, e.getStatus());
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.debug("WebSocket tunnel read failed due to I/O error.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
readThread.start();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(int i, String string) {
|
||||
try {
|
||||
if (tunnel != null)
|
||||
tunnel.close();
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.debug("Unable to close connection to guacd.", e);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the JavaScript Guacamole client makes a connection
|
||||
* request. It it up to the implementor of this function to define what
|
||||
* conditions must be met for a tunnel to be configured and returned as a
|
||||
* result of this connection request (whether some sort of credentials must
|
||||
* be specified, for example).
|
||||
*
|
||||
* @param request
|
||||
* The TunnelRequest associated with the connection request received.
|
||||
* Any parameters specified along with the connection request can be
|
||||
* read from this object.
|
||||
*
|
||||
* @return
|
||||
* A newly constructed GuacamoleTunnel if successful, null otherwise.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while constructing the GuacamoleTunnel, or if the
|
||||
* conditions required for connection are not met.
|
||||
*/
|
||||
protected abstract GuacamoleTunnel doConnect(TunnelRequest request)
|
||||
throws GuacamoleException;
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file has been modified from the original, upstream version to facilitate
|
||||
* integration with OpenUDS.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.tunnel.websocket.jetty8;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.guacamole.tunnel.TunnelRequest;
|
||||
import org.apache.guacamole.tunnel.TunnelRequestService;
|
||||
|
||||
/**
|
||||
* Tunnel servlet implementation which uses WebSocket as a tunnel backend,
|
||||
* rather than HTTP, properly parsing connection IDs included in the connection
|
||||
* request.
|
||||
*/
|
||||
@Singleton
|
||||
public class RestrictedGuacamoleWebSocketTunnelServlet extends GuacamoleWebSocketTunnelServlet {
|
||||
|
||||
/**
|
||||
* Service for handling tunnel requests.
|
||||
*/
|
||||
@Inject
|
||||
private TunnelRequestService tunnelRequestService;
|
||||
|
||||
@Override
|
||||
protected GuacamoleTunnel doConnect(TunnelRequest request)
|
||||
throws GuacamoleException {
|
||||
return tunnelRequestService.createTunnel(request);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.tunnel.websocket.jetty8;
|
||||
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import org.apache.guacamole.tunnel.TunnelLoader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Loads the Jetty 8 WebSocket tunnel implementation.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class WebSocketTunnelModule extends ServletModule implements TunnelLoader {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(WebSocketTunnelModule.class);
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
|
||||
try {
|
||||
|
||||
// Attempt to find WebSocket servlet
|
||||
Class.forName("org.apache.guacamole.tunnel.websocket.jetty8.RestrictedGuacamoleWebSocketTunnelServlet");
|
||||
|
||||
// Support found
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// If no such servlet class, this particular WebSocket support
|
||||
// is not present
|
||||
catch (ClassNotFoundException e) {}
|
||||
catch (NoClassDefFoundError e) {}
|
||||
|
||||
// Support not found
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureServlets() {
|
||||
|
||||
logger.info("Loading Jetty 8 WebSocket support...");
|
||||
serve("/websocket-tunnel").with(RestrictedGuacamoleWebSocketTunnelServlet.class);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jetty 8 WebSocket tunnel implementation. The classes here require Jetty 8.
|
||||
*/
|
||||
package org.apache.guacamole.tunnel.websocket.jetty8;
|
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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.tunnel.websocket.jetty9;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.eclipse.jetty.websocket.api.CloseStatus;
|
||||
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketListener;
|
||||
import org.apache.guacamole.GuacamoleClientException;
|
||||
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* WebSocket listener implementation which provides a Guacamole tunnel
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public abstract class GuacamoleWebSocketTunnelListener implements WebSocketListener {
|
||||
|
||||
/**
|
||||
* The default, minimum buffer size for instructions.
|
||||
*/
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(RestrictedGuacamoleWebSocketTunnelServlet.class);
|
||||
|
||||
/**
|
||||
* The underlying GuacamoleTunnel. WebSocket reads/writes will be handled
|
||||
* as reads/writes to this tunnel.
|
||||
*/
|
||||
private GuacamoleTunnel tunnel;
|
||||
|
||||
/**
|
||||
* Sends the given status on the given WebSocket connection and closes the
|
||||
* connection.
|
||||
*
|
||||
* @param session The outbound WebSocket connection to close.
|
||||
* @param guac_status The status to send.
|
||||
*/
|
||||
private void closeConnection(Session session, GuacamoleStatus guac_status) {
|
||||
|
||||
try {
|
||||
int code = guac_status.getWebSocketCode();
|
||||
String message = Integer.toString(guac_status.getGuacamoleStatusCode());
|
||||
session.close(new CloseStatus(code, message));
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.debug("Unable to close WebSocket connection.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new tunnel for the given session. How this tunnel is created
|
||||
* or retrieved is implementation-dependent.
|
||||
*
|
||||
* @param session The session associated with the active WebSocket
|
||||
* connection.
|
||||
* @return A connected tunnel, or null if no such tunnel exists.
|
||||
* @throws GuacamoleException If an error occurs while retrieving the
|
||||
* tunnel, or if access to the tunnel is denied.
|
||||
*/
|
||||
protected abstract GuacamoleTunnel createTunnel(Session session)
|
||||
throws GuacamoleException;
|
||||
|
||||
@Override
|
||||
public void onWebSocketConnect(final Session session) {
|
||||
|
||||
try {
|
||||
|
||||
// Get tunnel
|
||||
tunnel = createTunnel(session);
|
||||
if (tunnel == null) {
|
||||
closeConnection(session, GuacamoleStatus.RESOURCE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
|
||||
logger.debug("Error connecting WebSocket tunnel.", e);
|
||||
closeConnection(session, e.getStatus());
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare read transfer thread
|
||||
Thread readThread = new Thread() {
|
||||
|
||||
/**
|
||||
* Remote (client) side of this connection
|
||||
*/
|
||||
private final RemoteEndpoint remote = session.getRemote();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
StringBuilder buffer = new StringBuilder(BUFFER_SIZE);
|
||||
GuacamoleReader reader = tunnel.acquireReader();
|
||||
char[] readMessage;
|
||||
|
||||
try {
|
||||
|
||||
// Send tunnel UUID
|
||||
remote.sendString(new GuacamoleInstruction(
|
||||
GuacamoleTunnel.INTERNAL_DATA_OPCODE,
|
||||
tunnel.getUUID().toString()
|
||||
).toString());
|
||||
|
||||
try {
|
||||
|
||||
// Attempt to read
|
||||
while ((readMessage = reader.read()) != null) {
|
||||
|
||||
// Buffer message
|
||||
buffer.append(readMessage);
|
||||
|
||||
// Flush if we expect to wait or buffer is getting full
|
||||
if (!reader.available() || buffer.length() >= BUFFER_SIZE) {
|
||||
remote.sendString(buffer.toString());
|
||||
buffer.setLength(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// No more data
|
||||
closeConnection(session, GuacamoleStatus.SUCCESS);
|
||||
|
||||
}
|
||||
|
||||
// Catch any thrown guacamole exception and attempt
|
||||
// to pass within the WebSocket connection, logging
|
||||
// each error appropriately.
|
||||
catch (GuacamoleClientException e) {
|
||||
logger.info("WebSocket connection terminated: {}", e.getMessage());
|
||||
logger.debug("WebSocket connection terminated due to client error.", e);
|
||||
closeConnection(session, e.getStatus());
|
||||
}
|
||||
catch (GuacamoleConnectionClosedException e) {
|
||||
logger.debug("Connection to guacd closed.", e);
|
||||
closeConnection(session, GuacamoleStatus.SUCCESS);
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
|
||||
logger.debug("Internal error during connection to guacd.", e);
|
||||
closeConnection(session, e.getStatus());
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.debug("I/O error prevents further reads.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
readThread.start();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketText(String message) {
|
||||
|
||||
// Ignore inbound messages if there is no associated tunnel
|
||||
if (tunnel == null)
|
||||
return;
|
||||
|
||||
GuacamoleWriter writer = tunnel.acquireWriter();
|
||||
|
||||
try {
|
||||
// Write received message
|
||||
writer.write(message.toCharArray());
|
||||
}
|
||||
catch (GuacamoleConnectionClosedException e) {
|
||||
logger.debug("Connection to guacd closed.", e);
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.debug("WebSocket tunnel write failed.", e);
|
||||
}
|
||||
|
||||
tunnel.releaseWriter();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketBinary(byte[] payload, int offset, int length) {
|
||||
throw new UnsupportedOperationException("Binary WebSocket messages are not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketError(Throwable t) {
|
||||
|
||||
logger.debug("WebSocket tunnel closing due to error.", t);
|
||||
|
||||
try {
|
||||
if (tunnel != null)
|
||||
tunnel.close();
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.debug("Unable to close connection to guacd.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onWebSocketClose(int statusCode, String reason) {
|
||||
|
||||
try {
|
||||
if (tunnel != null)
|
||||
tunnel.close();
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.debug("Unable to close connection to guacd.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file has been modified from the original, upstream version to facilitate
|
||||
* integration with OpenUDS.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.tunnel.websocket.jetty9;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.UpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.api.UpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.apache.guacamole.tunnel.TunnelRequestService;
|
||||
|
||||
/**
|
||||
* WebSocketCreator which selects the appropriate WebSocketListener
|
||||
* implementation if the "guacamole" subprotocol is in use.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class RestrictedGuacamoleWebSocketCreator implements WebSocketCreator {
|
||||
|
||||
/**
|
||||
* Service for handling tunnel requests.
|
||||
*/
|
||||
private final TunnelRequestService tunnelRequestService;
|
||||
|
||||
/**
|
||||
* Creates a new WebSocketCreator which uses the given TunnelRequestService
|
||||
* to create new GuacamoleTunnels for inbound requests.
|
||||
*
|
||||
* @param tunnelRequestService The service to use for inbound tunnel
|
||||
* requests.
|
||||
*/
|
||||
public RestrictedGuacamoleWebSocketCreator(TunnelRequestService tunnelRequestService) {
|
||||
this.tunnelRequestService = tunnelRequestService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createWebSocket(UpgradeRequest request, UpgradeResponse response) {
|
||||
|
||||
// Validate and use "guacamole" subprotocol
|
||||
for (String subprotocol : request.getSubProtocols()) {
|
||||
|
||||
if ("guacamole".equals(subprotocol)) {
|
||||
response.setAcceptedSubProtocol(subprotocol);
|
||||
return new RestrictedGuacamoleWebSocketTunnelListener(tunnelRequestService);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Invalid protocol
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file has been modified from the original, upstream version to facilitate
|
||||
* integration with OpenUDS.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.tunnel.websocket.jetty9;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.guacamole.tunnel.TunnelRequestService;
|
||||
|
||||
/**
|
||||
* WebSocket listener implementation which properly parses connection IDs
|
||||
* included in the connection request.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class RestrictedGuacamoleWebSocketTunnelListener extends GuacamoleWebSocketTunnelListener {
|
||||
|
||||
/**
|
||||
* Service for handling tunnel requests.
|
||||
*/
|
||||
private final TunnelRequestService tunnelRequestService;
|
||||
|
||||
/**
|
||||
* Creates a new WebSocketListener which uses the given TunnelRequestService
|
||||
* to create new GuacamoleTunnels for inbound requests.
|
||||
*
|
||||
* @param tunnelRequestService The service to use for inbound tunnel
|
||||
* requests.
|
||||
*/
|
||||
public RestrictedGuacamoleWebSocketTunnelListener(TunnelRequestService tunnelRequestService) {
|
||||
this.tunnelRequestService = tunnelRequestService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuacamoleTunnel createTunnel(Session session) throws GuacamoleException {
|
||||
return tunnelRequestService.createTunnel(new WebSocketTunnelRequest(session.getUpgradeRequest()));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This file has been modified from the original, upstream version to facilitate
|
||||
* integration with OpenUDS.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.tunnel.websocket.jetty9;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.apache.guacamole.tunnel.TunnelRequestService;
|
||||
|
||||
/**
|
||||
* A WebSocketServlet partial re-implementation of GuacamoleTunnelServlet.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
@Singleton
|
||||
public class RestrictedGuacamoleWebSocketTunnelServlet extends WebSocketServlet {
|
||||
|
||||
/**
|
||||
* Service for handling tunnel requests.
|
||||
*/
|
||||
@Inject
|
||||
private TunnelRequestService tunnelRequestService;
|
||||
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory) {
|
||||
|
||||
// Register WebSocket implementation
|
||||
factory.setCreator(new RestrictedGuacamoleWebSocketCreator(tunnelRequestService));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.tunnel.websocket.jetty9;
|
||||
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import org.apache.guacamole.tunnel.TunnelLoader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Loads the Jetty 9 WebSocket tunnel implementation.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class WebSocketTunnelModule extends ServletModule implements TunnelLoader {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(WebSocketTunnelModule.class);
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
|
||||
try {
|
||||
|
||||
// Attempt to find WebSocket servlet
|
||||
Class.forName("org.apache.guacamole.tunnel.websocket.jetty9.RestrictedGuacamoleWebSocketTunnelServlet");
|
||||
|
||||
// Support found
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// If no such servlet class, this particular WebSocket support
|
||||
// is not present
|
||||
catch (ClassNotFoundException e) {}
|
||||
catch (NoClassDefFoundError e) {}
|
||||
|
||||
// Support not found
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureServlets() {
|
||||
|
||||
logger.info("Loading Jetty 9 WebSocket support...");
|
||||
serve("/websocket-tunnel").with(RestrictedGuacamoleWebSocketTunnelServlet.class);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.tunnel.websocket.jetty9;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.eclipse.jetty.websocket.api.UpgradeRequest;
|
||||
import org.apache.guacamole.tunnel.TunnelRequest;
|
||||
|
||||
/**
|
||||
* Jetty 9 WebSocket-specific implementation of TunnelRequest.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class WebSocketTunnelRequest extends TunnelRequest {
|
||||
|
||||
/**
|
||||
* All parameters passed via HTTP to the WebSocket handshake.
|
||||
*/
|
||||
private final Map<String, String[]> handshakeParameters;
|
||||
|
||||
/**
|
||||
* Creates a TunnelRequest implementation which delegates parameter and
|
||||
* session retrieval to the given UpgradeRequest.
|
||||
*
|
||||
* @param request The UpgradeRequest to wrap.
|
||||
*/
|
||||
public WebSocketTunnelRequest(UpgradeRequest request) {
|
||||
this.handshakeParameters = request.getParameterMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParameter(String name) {
|
||||
|
||||
// Pull list of values, if present
|
||||
List<String> values = getParameterValues(name);
|
||||
if (values == null || values.isEmpty())
|
||||
return null;
|
||||
|
||||
// Return first parameter value arbitrarily
|
||||
return values.get(0);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getParameterValues(String name) {
|
||||
|
||||
String[] values = handshakeParameters.get(name);
|
||||
if (values == null)
|
||||
return null;
|
||||
|
||||
return Arrays.asList(values);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jetty 9 WebSocket tunnel implementation. The classes here require at least
|
||||
* Jetty 9, prior to Jetty 9.1 (when support for JSR 356 was implemented).
|
||||
*/
|
||||
package org.apache.guacamole.tunnel.websocket.jetty9;
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Standard WebSocket tunnel implementation. The classes here require a recent
|
||||
* servlet container that supports JSR 356.
|
||||
*/
|
||||
package org.apache.guacamole.tunnel.websocket;
|
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* 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.tunnel.websocket.tomcat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.catalina.websocket.StreamInbound;
|
||||
import org.apache.catalina.websocket.WebSocketServlet;
|
||||
import org.apache.catalina.websocket.WsOutbound;
|
||||
import org.apache.guacamole.GuacamoleClientException;
|
||||
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
||||
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
||||
import org.apache.guacamole.tunnel.http.HTTPTunnelRequest;
|
||||
import org.apache.guacamole.tunnel.TunnelRequest;
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A WebSocketServlet partial re-implementation of GuacamoleTunnelServlet.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
|
||||
|
||||
/**
|
||||
* The default, minimum buffer size for instructions.
|
||||
*/
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelServlet.class);
|
||||
|
||||
/**
|
||||
* Sends the given status on the given WebSocket connection and closes the
|
||||
* connection.
|
||||
*
|
||||
* @param outbound The outbound WebSocket connection to close.
|
||||
* @param guac_status The status to send.
|
||||
*/
|
||||
public void closeConnection(WsOutbound outbound, GuacamoleStatus guac_status) {
|
||||
|
||||
try {
|
||||
byte[] message = Integer.toString(guac_status.getGuacamoleStatusCode()).getBytes("UTF-8");
|
||||
outbound.close(guac_status.getWebSocketCode(), ByteBuffer.wrap(message));
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.debug("Unable to close WebSocket tunnel.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String selectSubProtocol(List<String> subProtocols) {
|
||||
|
||||
// Search for expected protocol
|
||||
for (String protocol : subProtocols)
|
||||
if ("guacamole".equals(protocol))
|
||||
return "guacamole";
|
||||
|
||||
// Otherwise, fail
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamInbound createWebSocketInbound(String protocol,
|
||||
HttpServletRequest request) {
|
||||
|
||||
final TunnelRequest tunnelRequest = new HTTPTunnelRequest(request);
|
||||
|
||||
// Return new WebSocket which communicates through tunnel
|
||||
return new StreamInbound() {
|
||||
|
||||
/**
|
||||
* The GuacamoleTunnel associated with the connected WebSocket. If
|
||||
* the WebSocket has not yet been connected, this will be null.
|
||||
*/
|
||||
private GuacamoleTunnel tunnel = null;
|
||||
|
||||
@Override
|
||||
protected void onTextData(Reader reader) throws IOException {
|
||||
|
||||
// Ignore inbound messages if there is no associated tunnel
|
||||
if (tunnel == null)
|
||||
return;
|
||||
|
||||
GuacamoleWriter writer = tunnel.acquireWriter();
|
||||
|
||||
// Write all available data
|
||||
try {
|
||||
|
||||
char[] buffer = new char[BUFFER_SIZE];
|
||||
|
||||
int num_read;
|
||||
while ((num_read = reader.read(buffer)) > 0)
|
||||
writer.write(buffer, 0, num_read);
|
||||
|
||||
}
|
||||
catch (GuacamoleConnectionClosedException e) {
|
||||
logger.debug("Connection to guacd closed.", e);
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.debug("WebSocket tunnel write failed.", e);
|
||||
}
|
||||
|
||||
tunnel.releaseWriter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(final WsOutbound outbound) {
|
||||
|
||||
try {
|
||||
tunnel = doConnect(tunnelRequest);
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
|
||||
logger.debug("Error connecting WebSocket tunnel.", e);
|
||||
closeConnection(outbound, e.getStatus());
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not start connection if tunnel does not exist
|
||||
if (tunnel == null) {
|
||||
closeConnection(outbound, GuacamoleStatus.RESOURCE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
Thread readThread = new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
StringBuilder buffer = new StringBuilder(BUFFER_SIZE);
|
||||
GuacamoleReader reader = tunnel.acquireReader();
|
||||
char[] readMessage;
|
||||
|
||||
try {
|
||||
|
||||
// Send tunnel UUID
|
||||
outbound.writeTextMessage(CharBuffer.wrap(new GuacamoleInstruction(
|
||||
GuacamoleTunnel.INTERNAL_DATA_OPCODE,
|
||||
tunnel.getUUID().toString()
|
||||
).toString()));
|
||||
|
||||
try {
|
||||
|
||||
// Attempt to read
|
||||
while ((readMessage = reader.read()) != null) {
|
||||
|
||||
// Buffer message
|
||||
buffer.append(readMessage);
|
||||
|
||||
// Flush if we expect to wait or buffer is getting full
|
||||
if (!reader.available() || buffer.length() >= BUFFER_SIZE) {
|
||||
outbound.writeTextMessage(CharBuffer.wrap(buffer));
|
||||
buffer.setLength(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// No more data
|
||||
closeConnection(outbound, GuacamoleStatus.SUCCESS);
|
||||
|
||||
}
|
||||
|
||||
// Catch any thrown guacamole exception and attempt
|
||||
// to pass within the WebSocket connection, logging
|
||||
// each error appropriately.
|
||||
catch (GuacamoleClientException e) {
|
||||
logger.info("WebSocket connection terminated: {}", e.getMessage());
|
||||
logger.debug("WebSocket connection terminated due to client error.", e);
|
||||
closeConnection(outbound, e.getStatus());
|
||||
}
|
||||
catch (GuacamoleConnectionClosedException e) {
|
||||
logger.debug("Connection to guacd closed.", e);
|
||||
closeConnection(outbound, GuacamoleStatus.SUCCESS);
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
|
||||
logger.debug("Internal error during connection to guacd.", e);
|
||||
closeConnection(outbound, e.getStatus());
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.debug("I/O error prevents further reads.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
readThread.start();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(int i) {
|
||||
try {
|
||||
if (tunnel != null)
|
||||
tunnel.close();
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.debug("Unable to close connection to guacd.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBinaryData(InputStream in) throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the JavaScript Guacamole client makes a connection
|
||||
* request. It it up to the implementor of this function to define what
|
||||
* conditions must be met for a tunnel to be configured and returned as a
|
||||
* result of this connection request (whether some sort of credentials must
|
||||
* be specified, for example).
|
||||
*
|
||||
* @param request
|
||||
* The TunnelRequest associated with the connection request received.
|
||||
* Any parameters specified along with the connection request can be
|
||||
* read from this object.
|
||||
*
|
||||
* @return
|
||||
* A newly constructed GuacamoleTunnel if successful, null otherwise.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while constructing the GuacamoleTunnel, or if the
|
||||
* conditions required for connection are not met.
|
||||
*/
|
||||
protected abstract GuacamoleTunnel doConnect(TunnelRequest request)
|
||||
throws GuacamoleException;
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file has been modified from the original, upstream version to facilitate
|
||||
* integration with OpenUDS.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.tunnel.websocket.tomcat;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.guacamole.tunnel.TunnelRequest;
|
||||
import org.apache.guacamole.tunnel.TunnelRequestService;
|
||||
|
||||
/**
|
||||
* Tunnel servlet implementation which uses WebSocket as a tunnel backend,
|
||||
* rather than HTTP, properly parsing connection IDs included in the connection
|
||||
* request.
|
||||
*/
|
||||
@Singleton
|
||||
public class RestrictedGuacamoleWebSocketTunnelServlet extends GuacamoleWebSocketTunnelServlet {
|
||||
|
||||
/**
|
||||
* Service for handling tunnel requests.
|
||||
*/
|
||||
@Inject
|
||||
private TunnelRequestService tunnelRequestService;
|
||||
|
||||
@Override
|
||||
protected GuacamoleTunnel doConnect(TunnelRequest request)
|
||||
throws GuacamoleException {
|
||||
return tunnelRequestService.createTunnel(request);
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.tunnel.websocket.tomcat;
|
||||
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import org.apache.guacamole.tunnel.TunnelLoader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Loads the Jetty 9 WebSocket tunnel implementation.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public class WebSocketTunnelModule extends ServletModule implements TunnelLoader {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(WebSocketTunnelModule.class);
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
|
||||
try {
|
||||
|
||||
// Attempt to find WebSocket servlet
|
||||
Class.forName("org.apache.guacamole.tunnel.websocket.tomcat.RestrictedGuacamoleWebSocketTunnelServlet");
|
||||
|
||||
// Support found
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// If no such servlet class, this particular WebSocket support
|
||||
// is not present
|
||||
catch (ClassNotFoundException e) {}
|
||||
catch (NoClassDefFoundError e) {}
|
||||
|
||||
// Support not found
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureServlets() {
|
||||
|
||||
logger.info("Loading Tomcat 7 WebSocket support...");
|
||||
serve("/websocket-tunnel").with(RestrictedGuacamoleWebSocketTunnelServlet.class);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tomcat WebSocket tunnel implementation. The classes here require at least
|
||||
* Tomcat 7.0, and may change significantly as there is no common WebSocket
|
||||
* API for Java yet.
|
||||
*/
|
||||
package org.apache.guacamole.tunnel.websocket.tomcat;
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Virtual Cable S.L.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.openuds.guacamole;
|
||||
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
|
||||
import org.apache.guacamole.tunnel.TunnelRequestService;
|
||||
import org.openuds.guacamole.config.ConfigurationService;
|
||||
import org.openuds.guacamole.connection.ConnectionService;
|
||||
|
||||
/**
|
||||
* Guice module which binds classes required by the OpenUDS integration of
|
||||
* Apache Guacamole.
|
||||
*/
|
||||
public class UDSModule extends ServletModule {
|
||||
|
||||
@Override
|
||||
protected void configureServlets() {
|
||||
|
||||
// Serve servlets, etc. with Guice
|
||||
bind(GuiceContainer.class);
|
||||
|
||||
// Bind UDS-specific services
|
||||
bind(ConfigurationService.class);
|
||||
bind(ConnectionService.class);
|
||||
bind(TunnelRequestService.class);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Virtual Cable S.L.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.openuds.guacamole;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.servlet.GuiceServletContextListener;
|
||||
import org.apache.guacamole.tunnel.TunnelModule;
|
||||
|
||||
/**
|
||||
* ServletContextListener implementation which initializes Guice and services
|
||||
* specific to OpenUDS.
|
||||
*/
|
||||
public class UDSServletContextListener extends GuiceServletContextListener {
|
||||
|
||||
@Override
|
||||
protected Injector getInjector() {
|
||||
|
||||
// Create an injector with OpenUDS- and Guacamole-specific services
|
||||
// properly bound
|
||||
return Guice.createInjector(
|
||||
new UDSModule(),
|
||||
new TunnelModule()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Virtual Cable S.L.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.openuds.guacamole.config;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Properties;
|
||||
import javax.servlet.ServletContext;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Service that provides access to configuration information stored within
|
||||
* OpenUDS' tunnel.properties file.
|
||||
*/
|
||||
@Singleton
|
||||
public class ConfigurationService {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(ConfigurationService.class);
|
||||
|
||||
/**
|
||||
* The name of the property within tunnel.properties which defines the file
|
||||
* whose content dictates the base URL of the service providing connection
|
||||
* configuration information.
|
||||
*/
|
||||
private static final String UDSFILE_PROPERTY = "udsfile";
|
||||
|
||||
/**
|
||||
* The path beneath the OpenUDS service base URI (scheme + hostname) at
|
||||
* which the connection configuration service can be found. Currently, this
|
||||
* is hard-coded as "/guacamole/".
|
||||
*/
|
||||
private static final String UDS_CONNECTION_PATH = "/guacamole/";
|
||||
|
||||
/**
|
||||
* The base URI (scheme + hostname) where OpenUDS is being served.
|
||||
*/
|
||||
private final URI udsBaseURI;
|
||||
|
||||
/**
|
||||
* Parses the contents of the given file, reading the URI of the OpenUDS
|
||||
* service contained therein. The file is expected to define this URI on
|
||||
* the first line, and only the first line is read.
|
||||
*
|
||||
* @param udsFile
|
||||
* The file from which the URI of the OpenUDS service should be read.
|
||||
*
|
||||
* @return
|
||||
* The URI of the OpenUDS service.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the file could not be opened or read for any reason, or if the
|
||||
* line read from the file is not a valid URI.
|
||||
*/
|
||||
private URI readServiceURI(String udsFile) throws GuacamoleException {
|
||||
|
||||
// Open UDS file
|
||||
BufferedReader input;
|
||||
try {
|
||||
input = new BufferedReader(new FileReader(udsFile));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException("Failed to open UDS file.", e);
|
||||
}
|
||||
|
||||
// Parse the first line (and only the first line) assuming it contains
|
||||
// the URL of the OpenUDS service
|
||||
try {
|
||||
return new URI(input.readLine());
|
||||
}
|
||||
|
||||
// Rethrow general failure to read from the file
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException("Failed to read UDS service URI from file.", e);
|
||||
}
|
||||
|
||||
// Rethrow failure to parse the URL
|
||||
catch (URISyntaxException e) {
|
||||
throw new GuacamoleServerException("Failed to parse UDS service URI from file.", e);
|
||||
}
|
||||
|
||||
// Always close the file
|
||||
finally {
|
||||
|
||||
try {
|
||||
input.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.warn("Closure of OpenUDS file failed. Resource may leak.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an arbitrary URI, returns a new URI which contains only the scheme
|
||||
* and host. The path, fragment, etc. of the given URI, if any, are
|
||||
* discarded.
|
||||
*
|
||||
* @param uri
|
||||
* An arbitrary URI from which a base URI should be derived.
|
||||
*
|
||||
* @return
|
||||
* A new URI containing only the scheme and host of the provided URI.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the new URI could not be generated because the result is not a
|
||||
* valid URI.
|
||||
*/
|
||||
private URI getBaseURI(URI uri) throws GuacamoleException {
|
||||
|
||||
// Build base URI from only the scheme and host of the given URI
|
||||
try {
|
||||
return new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(),
|
||||
null, null, null);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
throw new GuacamoleServerException("Failed to derive base URI.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ConfigurationService which provides access to the
|
||||
* configuration information stored within the "/WEB-INF/tunnel.properties"
|
||||
* file in the classpath. This file will be parsed immediately, but any
|
||||
* resulting errors will simply be logged. If configuration information
|
||||
* cannot be read, attempts to retrieve this information later through calls
|
||||
* to the getters of this service will fail with appropriate exceptions.
|
||||
*
|
||||
* @param context
|
||||
* The ServletContext associated with the servlet container which is
|
||||
* serving this web application.
|
||||
*/
|
||||
@Inject
|
||||
public ConfigurationService(ServletContext context) {
|
||||
|
||||
// Read tunnel.properties
|
||||
Properties config = new Properties();
|
||||
try {
|
||||
config.load(context.getResourceAsStream("/WEB-INF/tunnel.properties"));
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Unable to read tunnel.properties.", e);
|
||||
}
|
||||
|
||||
// Parse URI from the UDS file (if defined)
|
||||
URI parsedURI = null;
|
||||
String udsFile = config.getProperty(UDSFILE_PROPERTY);
|
||||
if (udsFile != null) {
|
||||
|
||||
// Attempt to parse base URI from the UDS file, logging any failures
|
||||
try {
|
||||
parsedURI = getBaseURI(readServiceURI(udsFile));
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("OpenUDS service URI could not be parsed. This "
|
||||
+ "web application WILL NOT FUNCTION.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If no UDS file is defined, web application startup has failed
|
||||
else
|
||||
logger.error("Property \"{}\" not found within tunnel.properties. "
|
||||
+ "This web application WILL NOT FUNCTION.", UDSFILE_PROPERTY);
|
||||
|
||||
// Assign the parsed URI, which may be null
|
||||
udsBaseURI = parsedURI;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base URI of the OpenUDS service. All services providing data
|
||||
* to this Guacamole integration are hosted beneath this base URI.
|
||||
*
|
||||
* @return
|
||||
* The base URI of the OpenUDS service.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the base URI of the OpenUDS service is not defined because the
|
||||
* tunnel.properties file could not be parsed when the web application
|
||||
* started.
|
||||
*/
|
||||
public URI getUDSBaseURI() throws GuacamoleException {
|
||||
|
||||
// Explicitly fail if the configuration was not successfully read
|
||||
if (udsBaseURI == null)
|
||||
throw new GuacamoleServerException("The UDS base URI is not defined.");
|
||||
|
||||
return udsBaseURI;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path beneath the OpenUDS base URI at which the connection
|
||||
* configuration service can be found. This service is expected to respond
|
||||
* to HTTP GET requests, returning the configuration of requested
|
||||
* connections.
|
||||
*
|
||||
* @return
|
||||
* The path beneath the OpenUDS base URI at which the connection
|
||||
* configuration service can be found.
|
||||
*/
|
||||
public String getUDSConnectionPath() {
|
||||
return UDS_CONNECTION_PATH;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Virtual Cable S.L.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes used to retrieve OpenUDS-specific configuration information.
|
||||
*/
|
||||
package org.openuds.guacamole.config;
|
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Virtual Cable S.L.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.openuds.guacamole.connection;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.net.URLConnection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.protocol.GuacamoleConfiguration;
|
||||
import org.openuds.guacamole.config.ConfigurationService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Service which communicates with the remote OpenUDS connection service,
|
||||
* providing access to the underlying connection configuration.
|
||||
*/
|
||||
@Singleton
|
||||
public class ConnectionService {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(ConnectionService.class);
|
||||
|
||||
/**
|
||||
* The name of the parameter returned by the OpenUDS connection
|
||||
* configuration service which will contain the protocol that Guacamole
|
||||
* should use to initiate the remote desktop connection.
|
||||
*/
|
||||
private static final String PROTOCOL_PARAMETER = "protocol";
|
||||
|
||||
/**
|
||||
* Service for retrieving configuration information.
|
||||
*/
|
||||
@Inject
|
||||
private ConfigurationService configService;
|
||||
|
||||
/**
|
||||
* Makes an HTTP GET request to the OpenUDS service running at the given
|
||||
* URI, parsing the response into connection configuration data. The
|
||||
* response MUST be simple text, one line per connection parameter, with the
|
||||
* name of the connection parameter separated from the corresponding value
|
||||
* by a tab character. If the OpenUDS service encounters an error, it is
|
||||
* expected to return the single word "ERROR" on one line. Lines which do
|
||||
* not match these expectations will be skipped.
|
||||
*
|
||||
* @param uri
|
||||
* The URI of the OpenUDS service to which the HTTP GET request should
|
||||
* be made.
|
||||
*
|
||||
* @return
|
||||
* A map of all parameter name/value pairs returned by the OpenUDS
|
||||
* service.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the OpenUDS service returns an error, or the response from the
|
||||
* service cannot be read.
|
||||
*/
|
||||
private Map<String, String> readConnectionConfiguration(URI uri)
|
||||
throws GuacamoleException {
|
||||
|
||||
BufferedReader response;
|
||||
|
||||
// Connect to OpenUDS
|
||||
try {
|
||||
URLConnection connection = uri.toURL().openConnection();
|
||||
response = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException("Unable to open connection to OpenUDS service.", e);
|
||||
}
|
||||
|
||||
Map<String, String> parameters = new HashMap<String, String>();
|
||||
|
||||
// Read and parse each line of the response
|
||||
try {
|
||||
|
||||
String inputLine;
|
||||
while ((inputLine = response.readLine()) != null) {
|
||||
|
||||
// Abort upon error
|
||||
if (inputLine.equals("ERROR"))
|
||||
throw new GuacamoleServerException("OpenUDS service returned an error.");
|
||||
|
||||
// Determine separation between each line's key and value
|
||||
int tab = inputLine.indexOf('\t');
|
||||
if (tab == -1)
|
||||
continue;
|
||||
|
||||
// Add key/value pair from either side of the tab
|
||||
parameters.put(
|
||||
inputLine.substring(0, tab),
|
||||
inputLine.substring(tab + 1)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Rethrow any error which occurs during reading
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException("Failed to read response from OpenUDS service.", e);
|
||||
}
|
||||
|
||||
// Always close the stream
|
||||
finally {
|
||||
|
||||
try {
|
||||
response.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.warn("Closure of connection to OpenUDS failed. Resource may leak.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Parameters have been successfully parsed
|
||||
return parameters;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries OpenUDS for the connection configuration for the connection
|
||||
* associated with the given data. This data is an opaque value provided
|
||||
* via the "data" parameter to the Guacamole tunnel.
|
||||
*
|
||||
* @param data
|
||||
* The OpenUDS-specific data which defines the connection whose
|
||||
* configuration should be retrieved.
|
||||
*
|
||||
* @return
|
||||
* The configuration of the connection associated with the provided
|
||||
* OpenUDS-specific data.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the connection configuration could not be retrieved from OpenUDS,
|
||||
* of the response from OpenUDS was missing required information.
|
||||
*/
|
||||
public GuacamoleConfiguration getConnectionConfiguration(String data)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Build URI of remote service from the base URI and given data
|
||||
URI serviceURI = UriBuilder.fromUri(configService.getUDSBaseURI())
|
||||
.path(configService.getUDSConnectionPath())
|
||||
.path(data)
|
||||
.build();
|
||||
|
||||
// Pull connection configuration from remote service
|
||||
Map<String, String> params = readConnectionConfiguration(serviceURI);
|
||||
|
||||
// Pull the protocol from the parameters
|
||||
String protocol = params.remove(PROTOCOL_PARAMETER);
|
||||
if (protocol == null)
|
||||
throw new GuacamoleServerException("Protocol missing from OpenUDS response.");
|
||||
|
||||
// Create our configuration
|
||||
GuacamoleConfiguration config = new GuacamoleConfiguration();
|
||||
config.setProtocol(protocol);
|
||||
config.setParameters(params);
|
||||
|
||||
return config;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Virtual Cable S.L.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes used to communicate with OpenUDS' connection configuration web
|
||||
* service.
|
||||
*/
|
||||
package org.openuds.guacamole.connection;
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Virtual Cable S.L.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes associated with the OpenUDS integration of Apache Guacamole.
|
||||
*/
|
||||
package org.openuds.guacamole;
|
Loading…
Reference in New Issue
Block a user