/**
* Copyright 2003-2016 SSHTOOLS Limited. All Rights Reserved.
*
* For product documentation visit https://www.sshtools.com/
*
* This file is part of J2SSH Maverick.
*
* J2SSH Maverick is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* J2SSH Maverick is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with J2SSH Maverick. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sshtools.ssh2;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;
import com.sshtools.logging.Log;
import com.sshtools.ssh.ChannelEventListener;
import com.sshtools.ssh.ChannelOpenException;
import com.sshtools.ssh.ForwardingRequestListener;
import com.sshtools.ssh.PasswordAuthentication;
import com.sshtools.ssh.PublicKeyAuthentication;
import com.sshtools.ssh.SshAuthentication;
import com.sshtools.ssh.SshClient;
import com.sshtools.ssh.SshConnector;
import com.sshtools.ssh.SshContext;
import com.sshtools.ssh.SshException;
import com.sshtools.ssh.SshSession;
import com.sshtools.ssh.SshTransport;
import com.sshtools.ssh.SshTunnel;
import com.sshtools.ssh.components.SshKeyExchangeClient;
import com.sshtools.ssh.message.SshAbstractChannel;
import com.sshtools.util.ByteArrayReader;
import com.sshtools.util.ByteArrayWriter;
/**
* <p>
* Implementation of an <a href="../ssh/SshClient.html">SshClient</a> for the
* SSH2 protocol; this provides the ability to create custom channels and
* sending/receiving of global requests in addition to the standard <a
* href="../ssh/SshClient.html">SshClient</a> contract.
* </p>
*
* @author Lee David Painter
*/
public class Ssh2Client implements SshClient {
TransportProtocol transport;
SshTransport io;
AuthenticationProtocol authentication;
ConnectionProtocol connection;
String localIdentification;
String remoteIdentification;
String[] authenticationMethods;
String username;
Hashtable<String, ForwardingRequestListener> forwardingListeners = new Hashtable<String, ForwardingRequestListener>();
Hashtable<String, String> forwardingDestinations = new Hashtable<String, String>();
ForwardingRequestChannelFactory requestFactory = new ForwardingRequestChannelFactory();
SshAuthentication auth;
SshConnector connector;
boolean isXForwarding = false;
boolean buffered;
/**
* Default constructor called by <a
* href="../ssh/SshConnector.html">SshConnector</a>.
*
*/
public Ssh2Client() {
}
public void connect(SshTransport io, SshContext context,
SshConnector connector, String username,
String localIdentification, String remoteIdentification,
boolean buffered) throws SshException {
this.io = io;
this.localIdentification = localIdentification;
this.remoteIdentification = remoteIdentification;
this.username = username;
this.buffered = buffered;
this.connector = connector;
if (username == null) {
try {
io.close();
} catch (IOException ex) {
if (Log.isDebugEnabled()) {
Log.debug(
this,
"RECIEVED IOException IN Ssh2Client.connect:"
+ ex.getMessage());
}
}
throw new SshException("You must supply a valid username!",
SshException.BAD_API_USAGE);
}
if (!(context instanceof Ssh2Context)) {
try {
io.close();
} catch (IOException ex) {
if (Log.isDebugEnabled()) {
Log.debug(
this,
"RECIEVED IOException IN Ssh2Client.connect:"
+ ex.getMessage());
}
}
throw new SshException("Ssh2Context required!",
SshException.BAD_API_USAGE);
}
if (Log.isDebugEnabled()) {
Log.debug(
this,
"Connecting " + username + "@" + io.getHost() + ":"
+ io.getPort());
Log.debug(this, "Remote identification is "
+ remoteIdentification);
}
transport = new TransportProtocol();
if (Log.isDebugEnabled()) {
Log.debug(this, "Starting transport protocol");
}
transport.startTransportProtocol(io, (Ssh2Context) context,
localIdentification, remoteIdentification, this);
if (Log.isDebugEnabled()) {
Log.debug(this, "Starting authentication protocol");
}
authentication = new AuthenticationProtocol(transport);
authentication.setBannerDisplay(((Ssh2Context) context)
.getBannerDisplay());
connection = new ConnectionProtocol(this.transport, context, buffered);
connection.addChannelFactory(requestFactory);
getAuthenticationMethods(username);
if (Log.isDebugEnabled()) {
Log.debug(this, "SSH connection established");
}
}
/**
* Get a list of authentication methods for the user.
*
* @param username
* the name of the user
* @return an array of authentication methods, for example { "password",
* "publickey" }
* @throws SshException
*/
public String[] getAuthenticationMethods(String username)
throws SshException {
verifyConnection(false);
if (authenticationMethods == null) {
if (Log.isDebugEnabled()) {
Log.debug(this, "Requesting authentication methods");
}
String methods = authentication.getAuthenticationMethods(username,
ConnectionProtocol.SERVICE_NAME);
if (Log.isDebugEnabled()) {
Log.debug(this, "Available authentications are "
+ methods);
}
Vector<String> tmp = new Vector<String>();
int idx;
while (methods != null) {
idx = methods.indexOf(',');
if (idx > -1) {
tmp.addElement(methods.substring(0, idx));
methods = methods.substring(idx + 1);
} else {
tmp.addElement(methods);
methods = null;
}
}
authenticationMethods = new String[tmp.size()];
tmp.copyInto(authenticationMethods);
/*
* if there are no authentication methods, then check if
* isAuthenticated if isAuthenticated then need to start the message
* pump, as authenticate will not be called
*/
if (isAuthenticated()) {
connection.start();
}
}
return authenticationMethods;
}
/**
* this method is called if a user attempts password authentication it
* determines whether password authentication is possible. if it isnt, but
* keyboard interactive is possible, it authenticates using that instead
*/
private SshAuthentication checkForPasswordOverKBI(SshAuthentication auth) {
boolean kbiAuthenticationPossible = false;
for (int i = 0; i < authenticationMethods.length; i++) {
if (authenticationMethods[i].equals("password")) {
// password authentication is possible so return auth unchanged
return auth;
}
if ((authenticationMethods[i].equals("keyboard-interactive"))) {
// if none of the subsequent methods are password then have
// option to use kbi instead
kbiAuthenticationPossible = true;
}
}
// password is not possible, so attempt kbi
if (kbiAuthenticationPossible) {
// create KBIAuthentication instance
KBIAuthentication kbi = new KBIAuthentication();
// set the username that the user entered
kbi.setUsername(((PasswordAuthentication) auth).getUsername());
// set request handler, that sets the password the user entered as
// response to any prompts
kbi.setKBIRequestHandler(new KBIRequestHandlerWhenUserUsingPasswordAuthentication(
(PasswordAuthentication) auth));
return kbi;
}
// neither password nor kbi is possible so return auth unchanged so that
// the normal error message is returned
return auth;
}
/**
* <p>
* Request handler that sets the password the user entered as response to
* any prompts
* </p>
*
* @author David Hodgins
*/
private static class KBIRequestHandlerWhenUserUsingPasswordAuthentication
implements KBIRequestHandler {
private String password;
public KBIRequestHandlerWhenUserUsingPasswordAuthentication(
PasswordAuthentication pwdAuth) {
password = pwdAuth.getPassword();
}
/**
* Called by the <em>keyboard-interactive</em> authentication mechanism
* when the server requests information from the user. Each prompt
* should be displayed to the user with their response recorded within
* the prompt object.
*
* @param name
* @param instruction
* @param prompts
*/
public boolean showPrompts(String name, String instruction,
KBIPrompt[] prompts) {
for (int i = 0; i < prompts.length; i++) {
prompts[i].setResponse(password);
}
return true;
}
}
public int authenticate(SshAuthentication auth) throws SshException {
verifyConnection(false);
if (isAuthenticated())
throw new SshException(
"User is already authenticated! Did you check isAuthenticated?",
SshException.BAD_API_USAGE);
if (auth.getUsername() == null) {
auth.setUsername(username);
}
if (auth instanceof PasswordAuthentication
|| auth instanceof Ssh2PasswordAuthentication) {
auth = checkForPasswordOverKBI(auth);
}
if (Log.isDebugEnabled()) {
Log.debug(this, "Authenticating with " + auth.getMethod());
}
int result;
if (auth instanceof PasswordAuthentication
&& !(auth instanceof Ssh2PasswordAuthentication)) {
// We need to create an instance of Ssh2PasswordAuthentication
Ssh2PasswordAuthentication pwd = new Ssh2PasswordAuthentication();
pwd.setUsername(((PasswordAuthentication) auth).getUsername());
pwd.setPassword(((PasswordAuthentication) auth).getPassword());
result = authentication.authenticate(pwd,
ConnectionProtocol.SERVICE_NAME);
if (pwd.requiresPasswordChange()) {
disconnect();
throw new SshException("Password change required!",
SshException.CANCELLED_CONNECTION);
}
} else if (auth instanceof PublicKeyAuthentication
&& !(auth instanceof Ssh2PublicKeyAuthentication)) {
// We need to create an Ssh2PublicKeyAuthentication object
Ssh2PublicKeyAuthentication pk = new Ssh2PublicKeyAuthentication();
pk.setUsername(((PublicKeyAuthentication) auth).getUsername());
pk.setPublicKey(((PublicKeyAuthentication) auth).getPublicKey());
pk.setPrivateKey(((PublicKeyAuthentication) auth).getPrivateKey());
result = authentication.authenticate(pk,
ConnectionProtocol.SERVICE_NAME);
} else if (auth instanceof AuthenticationClient) {
// Execute an AuthenticationClient instance
result = authentication.authenticate((AuthenticationClient) auth,
ConnectionProtocol.SERVICE_NAME);
} else {
throw new SshException("Invalid authentication client",
SshException.BAD_API_USAGE);
}
if (result == SshAuthentication.COMPLETE) {
this.auth = auth;
connection.start();
}
if (Log.isDebugEnabled()) {
switch (result) {
case SshAuthentication.COMPLETE:
Log.debug(this, "Authentication complete");
break;
case SshAuthentication.FAILED:
Log.debug(this, "Authentication failed");
break;
case SshAuthentication.FURTHER_AUTHENTICATION_REQUIRED:
Log.debug(this,
"Authentication successful but further authentication required");
break;
case SshAuthentication.CANCELLED:
Log.debug(this, "Authentication cancelled");
break;
case SshAuthentication.PUBLIC_KEY_ACCEPTABLE:
Log.debug(this,
"Server accepts the public key provided");
break;
default:
Log.debug(this, "Unknown authentication result "
+ result);
break;
}
}
return result;
}
public boolean isAuthenticated() {
return authentication.isAuthenticated();
}
public void disconnect() {
try {
if (Log.isDebugEnabled()) {
Log.debug(this, "Disconnecting");
}
connection.signalClosingState();
connection.stop();
transport.disconnect(TransportProtocol.BY_APPLICATION,
"The user disconnected the application");
} catch (Throwable t) {
}
if (Log.isDebugEnabled()) {
Log.debug(this, "Disconnected");
}
}
public void exit() {
try {
if (Log.isDebugEnabled()) {
Log.debug(this, "Disconnecting");
}
connection.signalClosingState();
transport.disconnect(TransportProtocol.BY_APPLICATION,
"The user disconnected the application");
} catch (Throwable t) {
}
if (Log.isDebugEnabled()) {
Log.debug(this, "Disconnected");
}
}
public boolean isConnected() {
return transport.isConnected();
}
/**
* The SSH transport protocol exchanges keys at the beginning of the
* session; the specification recommends that these keys be re-exchanged
* after each gigabyte of transmitted data or after each hour of connection
* time, whichever comes sooner. This method can be called at anytime to
* begin the key exchange process.
*
* @throws SshException
*/
public void forceKeyExchange() throws SshException {
if (Log.isDebugEnabled()) {
Log.debug(this, "Forcing key exchange");
}
transport.sendKeyExchangeInit(false);
}
public SshSession openSessionChannel() throws SshException,
ChannelOpenException {
return openSessionChannel(32768, 32768, null);
}
public SshSession openSessionChannel(long timeout) throws SshException,
ChannelOpenException {
return openSessionChannel(32768, 32768, null, timeout);
}
public SshSession openSessionChannel(ChannelEventListener listener,
long timeout) throws SshException, ChannelOpenException {
return openSessionChannel(32768, 32768, listener, timeout);
}
public SshSession openSessionChannel(ChannelEventListener listener)
throws SshException, ChannelOpenException {
return openSessionChannel(32768, 32768, listener);
}
/**
* Additional method to open a session with SSH2 specific features.
*
* @param windowspace
* the initial amount of window space available
* @param packetsize
* the maximum packet size
* @param listener
* an event listener to add before opening
* @return an open session
* @throws SshException
* @throws ChannelOpenException
*/
public Ssh2Session openSessionChannel(int windowspace, int packetsize,
ChannelEventListener listener) throws ChannelOpenException,
SshException {
return openSessionChannel(windowspace, packetsize, listener, 0);
}
public Ssh2Session openSessionChannel(int windowspace, int packetsize,
ChannelEventListener listener, long timeout)
throws ChannelOpenException, SshException {
verifyConnection(true);
if (Log.isDebugEnabled()) {
Log.debug(this, "Opening session channel windowspace="
+ windowspace + " packetsize=" + packetsize);
}
Ssh2Session channel = new Ssh2Session(windowspace, packetsize, this);
if (listener != null) {
channel.addChannelEventListener(listener);
}
connection.openChannel(channel, null, timeout);
if (Log.isDebugEnabled()) {
Log.debug(this, "Channel has been opened channelid="
+ channel.getChannelId());
}
/**
* Do our sessions require x forwarding? If so then request and make
* sure our XForwarding Channel Factory is active.
*/
if (connection.getContext().getX11Display() != null) {
String display = connection.getContext().getX11Display();
int idx = display.indexOf(':');
int screen = 0;
if (idx != -1) {
display = display.substring(idx + 1);
}
idx = display.indexOf('.');
if (idx > -1) {
screen = Integer.parseInt(display.substring(idx + 1));
}
byte[] x11FakeCookie = connection.getContext()
.getX11AuthenticationCookie();
StringBuffer cookieBuf = new StringBuffer();
for (int i = 0; i < 16; i++) {
String b = Integer.toHexString(x11FakeCookie[i] & 0xff);
if (b.length() == 1) {
b = "0" + b;
}
cookieBuf.append(b);
}
if (channel.requestX11Forwarding(false, "MIT-MAGIC-COOKIE-1",
cookieBuf.toString(), screen)) {
isXForwarding = true;
}
}
return channel;
}
public SshClient openRemoteClient(String hostname, int port,
String username, SshConnector con) throws SshException,
ChannelOpenException {
if (Log.isDebugEnabled()) {
Log.debug(this,
"Opening a remote SSH client from " + io.getHost() + " to "
+ username + "@" + hostname + ":" + port);
}
SshTunnel tunnel = openForwardingChannel(hostname, port, "127.0.0.1",
22, "127.0.0.1", 22, null, null);
return con.connect(tunnel, username, buffered);
}
public SshClient openRemoteClient(String hostname, int port, String username)
throws SshException, ChannelOpenException {
return openRemoteClient(hostname, port, username, connector);
}
public SshTunnel openForwardingChannel(String hostname, int port,
String listeningAddress, int listeningPort, String originatingHost,
int originatingPort, SshTransport transport,
ChannelEventListener listener) throws SshException,
ChannelOpenException {
ByteArrayWriter request = new ByteArrayWriter();
try {
if (Log.isDebugEnabled()) {
Log.debug(this, "Opening forwarding channel from "
+ listeningAddress + ":" + listeningPort + " to "
+ hostname + ":" + port);
}
Ssh2ForwardingChannel tunnel = new Ssh2ForwardingChannel(
Ssh2ForwardingChannel.LOCAL_FORWARDING_CHANNEL, 32768,
2097152, hostname, port, listeningAddress, listeningPort,
originatingHost, originatingPort, transport);
request.writeString(hostname);
request.writeInt(port);
request.writeString(originatingHost);
request.writeInt(originatingPort);
tunnel.addChannelEventListener(listener);
openChannel(tunnel, request.toByteArray());
return tunnel;
} catch (IOException ex) {
throw new SshException(ex, SshException.INTERNAL_ERROR);
} finally {
try {
request.close();
} catch (IOException e) {
}
}
}
public boolean requestRemoteForwarding(String bindAddress, int bindPort,
String hostToConnect, int portToConnect,
ForwardingRequestListener listener) throws SshException {
ByteArrayWriter baw = new ByteArrayWriter();
try {
if (listener == null) {
throw new SshException(
"You must specify a listener to receive connection requests",
SshException.BAD_API_USAGE);
}
if (Log.isDebugEnabled()) {
Log.debug(this, "Requesting remote forwarding from "
+ bindAddress + ":" + bindPort + " to " + hostToConnect
+ ":" + portToConnect);
}
baw.writeString(bindAddress);
baw.writeInt(bindPort);
GlobalRequest request = new GlobalRequest("tcpip-forward",
baw.toByteArray());
if (sendGlobalRequest(request, true)) {
forwardingListeners.put(
(bindAddress + ":" + String.valueOf(bindPort)),
listener);
forwardingDestinations.put(
(bindAddress + ":" + String.valueOf(bindPort)),
(hostToConnect + ":" + String.valueOf(portToConnect)));
// Setup the forwarding listener
return true;
}
return false;
} catch (IOException ex) {
throw new SshException(ex, SshException.INTERNAL_ERROR);
} finally {
try {
baw.close();
} catch (IOException e) {
}
}
}
public boolean cancelRemoteForwarding(String bindAddress, int bindPort)
throws SshException {
ByteArrayWriter baw = new ByteArrayWriter();
try {
if (Log.isDebugEnabled()) {
Log.debug(this, "Cancelling remote forwarding from "
+ bindAddress + ":" + bindPort);
}
baw.writeString(bindAddress);
baw.writeInt(bindPort);
GlobalRequest request = new GlobalRequest("cancel-tcpip-forward",
baw.toByteArray());
if (sendGlobalRequest(request, true)) {
forwardingListeners.remove(bindAddress + ":"
+ String.valueOf(bindPort));
forwardingDestinations.remove(bindAddress + ":"
+ String.valueOf(bindPort));
return true;
}
return false;
} catch (IOException ex) {
throw new SshException(ex, SshException.INTERNAL_ERROR);
} finally {
try {
baw.close();
} catch (IOException e) {
}
}
}
/**
* Additional method to open a custom SSH2 channel.
*
* @param channel
* the channel to open
* @param requestdata
* the request data
* @throws SshException
* @throws ChannelOpenException
*/
public void openChannel(Ssh2Channel channel, byte[] requestdata)
throws SshException, ChannelOpenException {
verifyConnection(true);
connection.openChannel(channel, requestdata);
}
/**
* Additional method to open a custom SSH2 channel.
*
* @param channel
* the channel to open
* @throws SshException
* @throws ChannelOpenException
*/
public void openChannel(SshAbstractChannel channel) throws SshException,
ChannelOpenException {
verifyConnection(true);
if (channel instanceof Ssh2Channel) {
connection.openChannel((Ssh2Channel) channel, null);
} else {
throw new SshException("The channel is not an SSH2 channel!",
SshException.BAD_API_USAGE);
}
}
/**
* Installs a custom channel factory so that the client may respond to
* channel open requests.
*
* @param factory
* the channel factory
* @throws SshException
*/
public void addChannelFactory(ChannelFactory factory) throws SshException {
connection.addChannelFactory(factory);
}
public SshContext getContext() {
return transport.transportContext;
}
/**
* Installs a global request handler so that the client may respond to
* global requests.
*
* @param handler
* the global request handler
* @throws SshException
*/
public void addRequestHandler(GlobalRequestHandler handler)
throws SshException {
if (Log.isDebugEnabled()) {
String requests = "";
for (int i = 0; i < handler.supportedRequests().length; i++)
requests += handler.supportedRequests()[i] + " ";
Log.debug(this, "Installing global request handler for "
+ requests.trim());
}
connection.addRequestHandler(handler);
}
/**
* Sends a global request to the remote side.
*
* @param request
* the global request
* @param wantreply
* specifies whether the remote side should send a
* success/failure message
* @return <code>true</code> if the request succeeded and wantreply=true,
* otherwise <code>false</code>
* @throws SshException
*/
public boolean sendGlobalRequest(GlobalRequest request, boolean wantreply)
throws SshException {
verifyConnection(true);
return connection.sendGlobalRequest(request, wantreply);
}
public String getRemoteIdentification() {
return remoteIdentification;
}
void verifyConnection(boolean requireAuthentication) throws SshException {
if (authentication == null || transport == null || connection == null) {
throw new SshException("Not connected!", SshException.BAD_API_USAGE);
}
if (!transport.isConnected()) {
throw new SshException("The connection has been terminated!",
SshException.REMOTE_HOST_DISCONNECTED);
}
if (!authentication.isAuthenticated() && requireAuthentication) {
throw new SshException("The connection is not authenticated!",
SshException.BAD_API_USAGE);
}
}
public String getUsername() {
return username;
}
public SshClient duplicate() throws SshException {
if ((username == null || auth == null)) {
throw new SshException(
"Cannot duplicate! The existing connection does not have a set of credentials",
SshException.BAD_API_USAGE);
}
try {
if (Log.isDebugEnabled()) {
Log.debug(this, "Duplicating SSH client");
}
SshClient duplicate = connector.connect(io.duplicate(), username,
buffered, transport.transportContext);
if (duplicate.authenticate(auth) != SshAuthentication.COMPLETE) {
duplicate.disconnect();
throw new SshException(
"Duplication attempt failed to authenicate user!",
SshException.INTERNAL_ERROR);
}
return duplicate;
} catch (IOException ex) {
throw new SshException(ex, SshException.CONNECT_FAILED);
}
}
class ForwardingRequestChannelFactory implements ChannelFactory {
String[] types = new String[] {
Ssh2ForwardingChannel.REMOTE_FORWARDING_CHANNEL, "x11" };
public String[] supportedChannelTypes() {
return types;
}
/**
* <p>
* Create an instance of an SSH channel. The new instance should be
* returned, if for any reason the channel cannot be created either
* because the channel is not supported or there are not enough
* resources an exception is thrown.
* </p>
*
* @param channeltype
* @param requestdata
* @return an open channel
* @throws ChannelOpenException
*/
public Ssh2Channel createChannel(String channeltype, byte[] requestdata)
throws SshException, ChannelOpenException {
if (channeltype
.equals(Ssh2ForwardingChannel.REMOTE_FORWARDING_CHANNEL)) {
ByteArrayReader bar = new ByteArrayReader(requestdata);
try {
String address = bar.readString();
int port = (int) bar.readInt();
String originatorIP = bar.readString();
int originatorPort = (int) bar.readInt();
String key = address + ":" + String.valueOf(port);
if (forwardingListeners.containsKey(key)) {
ForwardingRequestListener listener = (ForwardingRequestListener) forwardingListeners
.get(key);
String destination = (String) forwardingDestinations
.get(key);
String hostToConnect = destination.substring(0,
destination.indexOf(':'));
int portToConnect = Integer.parseInt(destination
.substring(destination.indexOf(':') + 1));
if (Log.isDebugEnabled()) {
Log.debug(this,
"Creating remote forwarding channel from "
+ address + ":" + port + " to "
+ hostToConnect + ":"
+ portToConnect);
}
// create connection from here to end point of tunnel,
// then pass to new Ssh2ForwardingChannel
Ssh2ForwardingChannel channel = new Ssh2ForwardingChannel(
Ssh2ForwardingChannel.REMOTE_FORWARDING_CHANNEL,
32768, 2097152, hostToConnect, portToConnect,
address, port, originatorIP, originatorPort,
listener.createConnection(hostToConnect,
portToConnect));
listener.initializeTunnel(channel);
return channel;
}
throw new ChannelOpenException(
"Forwarding had not previously been requested",
ChannelOpenException.ADMINISTRATIVIVELY_PROHIBITED);
} catch (IOException ex) {
throw new ChannelOpenException(ex.getMessage(),
ChannelOpenException.RESOURCE_SHORTAGE);
} catch (SshException ex) {
throw new ChannelOpenException(ex.getMessage(),
ChannelOpenException.CONNECT_FAILED);
} finally {
try {
bar.close();
} catch (IOException e) {
}
}
} else if (channeltype.equals("x11")) {
if (!isXForwarding)
throw new ChannelOpenException(
"X Forwarding had not previously been requested",
ChannelOpenException.ADMINISTRATIVIVELY_PROHIBITED);
ByteArrayReader bar = new ByteArrayReader(requestdata);
try {
String originatorIP = bar.readString();
int originatorPort = (int) bar.readInt();
String display = connection.getContext().getX11Display();
int i = display.indexOf(":");
String targetAddr;
int targetPort;
int num = 0;
int screen = 0;
if (i != -1) {
targetAddr = display.substring(0, i);
display = display.substring(i + 1);
i = display.indexOf('.');
if (i > -1) {
num = Integer.parseInt(display.substring(0, i));
screen = Integer.parseInt(display.substring(i + 1));
} else
num = Integer.parseInt(display);
targetPort = num;
} else {
targetAddr = display;
targetPort = 6000;
}
if (targetPort <= 10) {
targetPort += 6000;
}
if (Log.isDebugEnabled()) {
Log.debug(this,
"Creating X11 forwarding channel for display "
+ targetAddr + ":" + screen);
}
ForwardingRequestListener listener = connection
.getContext().getX11RequestListener();
Ssh2ForwardingChannel channel = new Ssh2ForwardingChannel(
Ssh2ForwardingChannel.X11_FORWARDING_CHANNEL,
32768, 32768, targetAddr, targetPort, targetAddr, // This
// will
// get
// set
// as
// the
// forwarding
// key
screen, // This will get set as the forwarding key
originatorIP, originatorPort,
listener.createConnection(targetAddr, targetPort));
listener.initializeTunnel(channel);
return channel;
} catch (Throwable ex) {
throw new ChannelOpenException(ex.getMessage(),
ChannelOpenException.CONNECT_FAILED);
} finally {
try {
bar.close();
} catch (IOException e) {
}
}
}
throw new ChannelOpenException(channeltype + " is not supported",
ChannelOpenException.UNKNOWN_CHANNEL_TYPE);
}
}
public int getChannelCount() {
return connection.getChannelCount();
}
public int getVersion() {
return 2;
}
public boolean isBuffered() {
return buffered;
}
/**
* Returns the key exchange algorithm last used.
*
* @return String
*/
public String getKeyExchangeInUse() {
return (transport.keyExchange == null ? "none" : transport.keyExchange
.getAlgorithm());
}
public SshKeyExchangeClient getKeyExchangeInstanceInUse() {
return transport.keyExchange;
}
/**
* Returns the host key algorithm used in the last key exchange.
*
* @return String
*/
public String getHostKeyInUse() {
return (transport.hostkey == null ? "none" : transport.hostkey
.getAlgorithm());
}
/**
* Get the cipher algorithm used to encrypt data sent to the server.
*
* @return String
*/
public String getCipherInUseCS() {
return (transport.encryption == null ? "none" : transport.encryption
.getAlgorithm());
}
/**
* Get the cipher algorithm used to decrypt data received from the server.
*
* @return String
*/
public String getCipherInUseSC() {
return (transport.decryption == null ? "none" : transport.decryption
.getAlgorithm());
}
/**
* Get the MAC algorithm used to verify data sent by the client.
*
* @return String
*/
public String getMacInUseCS() {
return (transport.outgoingMac == null ? "none" : transport.outgoingMac
.getAlgorithm());
}
/**
* Get the MAC algorithm used to verify data sent by the server.
*
* @return String
*/
public String getMacInUseSC() {
return (transport.incomingMac == null ? "none" : transport.incomingMac
.getAlgorithm());
}
/**
* Get the compression algorithm used to compress the clients outgoing data.
*
* @return String
*/
public String getCompressionInUseCS() {
return (transport.outgoingCompression == null ? "none"
: transport.outgoingCompression.getAlgorithm());
}
/**
* Get the compression algorithm used to decompress the servers data.
*
* @return String
*/
public String getCompressionInUseSC() {
return (transport.incomingCompression == null ? "none"
: transport.incomingCompression.getAlgorithm());
}
public String toString() {
return "SSH2 "
+ io.getHost()
+ ":"
+ io.getPort()
+ " [kex="
+ (transport.keyExchange == null ? "none"
: transport.keyExchange.getAlgorithm())
+ " hostkey="
+ (transport.hostkey == null ? "none" : transport.hostkey
.getAlgorithm())
+ " client->server="
+ (transport.encryption == null ? "none" : transport.encryption
.getAlgorithm())
+ ","
+ (transport.outgoingMac == null ? "none"
: transport.outgoingMac.getAlgorithm())
+ ","
+ (transport.outgoingCompression == null ? "none"
: transport.outgoingCompression.getAlgorithm())
+ " server->client="
+ (transport.decryption == null ? "none" : transport.decryption
.getAlgorithm())
+ ","
+ (transport.incomingMac == null ? "none"
: transport.incomingMac.getAlgorithm())
+ ","
+ (transport.incomingCompression == null ? "none"
: transport.incomingCompression.getAlgorithm()) + "]";
}
}