com.sshtools.ssh2.Ssh2ForwardingChannel Maven / Gradle / Ivy
The newest version!
/**
* 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 .
*/
package com.sshtools.ssh2;
import java.io.IOException;
import com.sshtools.ssh.SshException;
import com.sshtools.ssh.SshIOException;
import com.sshtools.ssh.SshTransport;
import com.sshtools.ssh.SshTunnel;
import com.sshtools.ssh.message.SshChannelMessage;
/**
*
* @author Lee David Painter
*/
class Ssh2ForwardingChannel extends Ssh2Channel implements SshTunnel {
public static final String X11_FORWARDING_CHANNEL = "x11";
public static final String LOCAL_FORWARDING_CHANNEL = "direct-tcpip";
public static final String REMOTE_FORWARDING_CHANNEL = "forwarded-tcpip";
protected final static String X11AUTH_PROTO = "MIT-MAGIC-COOKIE-1";
SshTransport transport;
String host;
int port;
String listeningAddress;
int listeningPort;
String originatingHost;
int originatingPort;
byte[] buf = new byte[1024];
boolean hasSpoofedCookie = false;
int idx = 0;
int requiredLength = 12; // header len
int protocolLength;
int cookieLength;
/**
* @param name
* @param windowsize
* @param packetsize
*/
public Ssh2ForwardingChannel(String name, int remotewindow,
int remotepacket, String host, int port, String listeningAddress,
int listeningPort, String originatingHost, int originatingPort,
SshTransport transport) {
super(name, remotewindow, remotepacket);
this.transport = transport;
this.host = host;
this.port = port;
this.listeningAddress = listeningAddress;
this.listeningPort = listeningPort;
this.originatingHost = originatingHost;
this.originatingPort = originatingPort;
}
public String getHost() {
return host;
}
public String getConnectedHost() {
return getHost();
}
public int getPort() {
return port;
}
public String getOriginatingHost() {
return originatingHost;
}
public int getOriginatingPort() {
return originatingPort;
}
public String getListeningAddress() {
return listeningAddress;
}
public int getListeningPort() {
return listeningPort;
}
public boolean isLocal() {
return getName().equals(Ssh2ForwardingChannel.LOCAL_FORWARDING_CHANNEL);
}
public boolean isX11() {
return getName().equals(Ssh2ForwardingChannel.X11_FORWARDING_CHANNEL);
}
public SshTransport getTransport() {
return transport;
}
public boolean isLocalEOF() {
return isLocalEOF;
}
public boolean isRemoteEOF() {
return isRemoteEOF;
}
public SshTransport duplicate() throws IOException {
throw new SshIOException(
new SshException("SSH tunnels cannot be duplicated!",
SshException.BAD_API_USAGE));
}
public void close() {
// DEBUG START
/*
* System.out.println(getName() + " id=" + channelid + " rid=" +
* remoteid + " CLOSING localwindow=" + localwindow.available() +
* " remotewindow=" + remotewindow.available());
*/
// DEBUG END
super.close();
}
protected void processStandardData(int len, SshChannelMessage msg)
throws SshException {
// System.out.println(getName() + " id=" + channelid + " rid=" +
// remoteid + " localwindow=" + localwindow.available() +
// " remotewindow=" + remotewindow.available());
if (getName().equals(X11_FORWARDING_CHANNEL)) {
if (!hasSpoofedCookie) {
int n;
if (idx < 12) {
n = readMore(msg);
len -= n;
if (requiredLength == 0) {
if (buf[0] == 0x42) {
protocolLength = ((buf[6] & 0xff) << 8)
| (buf[7] & 0xff);
cookieLength = ((buf[8] & 0xff) << 8)
| (buf[9] & 0xff);
} else if (buf[0] == 0x6c) {
protocolLength = ((buf[7] & 0xff) << 8)
| (buf[6] & 0xff);
cookieLength = ((buf[9] & 0xff) << 8)
| (buf[8] & 0xff);
} else {
close();
throw new SshException(
"Corrupt X11 authentication packet",
SshException.CHANNEL_FAILURE);
}
requiredLength = (protocolLength + 0x03) & ~0x03;
requiredLength += (cookieLength + 0x03) & ~0x03;
if (requiredLength + idx > buf.length) {
close();
throw new SshException(
"Corrupt X11 authentication packet",
SshException.CHANNEL_FAILURE);
}
if (requiredLength == 0) {
close();
throw new SshException(
"X11 authentication cookie not found",
SshException.CHANNEL_FAILURE);
}
}
}
// Read payload of authentication packet
//
if (len > 0) {
n = readMore(msg);
len -= n;
if (requiredLength == 0) {
byte[] fakeCookie = connection.getContext()
.getX11AuthenticationCookie();
String protoStr = new String(buf, 12, protocolLength);
byte[] recCookie = new byte[fakeCookie.length];
protocolLength = ((protocolLength + 0x03) & ~0x03);
System.arraycopy(buf, 12 + protocolLength, recCookie,
0, fakeCookie.length);
if (!X11AUTH_PROTO.equals(protoStr)
|| !compareCookies(fakeCookie, recCookie,
fakeCookie.length)) {
close();
throw new SshException("Incorrect X11 cookie",
SshException.CHANNEL_FAILURE);
}
byte[] realCookie = connection.getContext()
.getX11RealCookie();
if (realCookie.length != cookieLength) {
throw new SshException("Invalid X11 cookie",
SshException.CHANNEL_FAILURE);
}
System.arraycopy(realCookie, 0, buf,
12 + protocolLength, realCookie.length);
hasSpoofedCookie = true;
super.processStandardData(len, msg);
buf = null;
}
}
if (!hasSpoofedCookie || len == 0) {
return;
}
}
}
super.processStandardData(len, msg);
}
private boolean compareCookies(byte[] src, byte[] dst, int len) {
int i = 0;
for (; i < len; i++) {
if (src[i] != dst[i]) {
break;
}
}
return i == len;
}
private int readMore(SshChannelMessage msg) {
int len = msg.available();
if (len > requiredLength) {
msg.read(buf, idx, requiredLength);
idx += requiredLength;
len = requiredLength;
requiredLength = 0;
} else {
msg.read(buf, idx, len);
idx += len;
requiredLength -= len;
}
return len;
}
}