ChannelForwardedTCPIP.java
/*
* Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
*
* 2. 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.
*
* 3. The names of the authors may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE 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 com.jcraft.jsch;
import java.io.PipedOutputStream;
import java.net.Socket;
import java.util.Vector;
public class ChannelForwardedTCPIP extends Channel {
private static Vector<Config> pool = new Vector<>();
private static final int LOCAL_WINDOW_SIZE_MAX = 0x20000;
// static private final int LOCAL_WINDOW_SIZE_MAX=0x100000;
private static final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000;
private static final int TIMEOUT = 10 * 1000;
private Socket socket = null;
private ForwardedTCPIPDaemon daemon = null;
private Config config = null;
ChannelForwardedTCPIP() {
super();
lwsize_max = LOCAL_WINDOW_SIZE_MAX;
lwsize = LOCAL_WINDOW_SIZE_MAX;
lmpsize = LOCAL_MAXIMUM_PACKET_SIZE;
io = new IO();
connected = true;
}
@Override
public void run() {
try {
if (config instanceof ConfigDaemon) {
ConfigDaemon _config = (ConfigDaemon) config;
Class<? extends ForwardedTCPIPDaemon> c =
Class.forName(_config.target).asSubclass(ForwardedTCPIPDaemon.class);
daemon = c.getDeclaredConstructor().newInstance();
PipedOutputStream out = new PipedOutputStream();
io.setInputStream(new PassiveInputStream(out, 32 * 1024), false);
daemon.setChannel(this, getInputStream(), out);
daemon.setArg(_config.arg);
new Thread(daemon).start();
} else {
ConfigLHost _config = (ConfigLHost) config;
socket =
(_config.factory == null) ? Util.createSocket(_config.target, _config.lport, TIMEOUT)
: _config.factory.createSocket(_config.target, _config.lport);
socket.setTcpNoDelay(true);
io.setInputStream(socket.getInputStream());
io.setOutputStream(socket.getOutputStream());
}
sendOpenConfirmation();
} catch (Exception e) {
sendOpenFailure(SSH_OPEN_ADMINISTRATIVELY_PROHIBITED);
close = true;
disconnect();
return;
}
thread = Thread.currentThread();
Buffer buf = new Buffer(rmpsize);
Packet packet = new Packet(buf);
int i = 0;
try {
Session _session = getSession();
while (thread != null && io != null && io.in != null) {
i = io.in.read(buf.buffer, 14, buf.buffer.length - 14 - _session.getBufferMargin());
if (i <= 0) {
eof();
break;
}
packet.reset();
buf.putByte((byte) Session.SSH_MSG_CHANNEL_DATA);
buf.putInt(recipient);
buf.putInt(i);
buf.skip(i);
synchronized (this) {
if (close)
break;
_session.write(packet, this, i);
}
}
} catch (Exception e) {
// System.err.println(e);
}
// thread=null;
// eof();
disconnect();
}
@Override
void getData(Buffer buf) {
setRecipient(buf.getInt());
setRemoteWindowSize(buf.getUInt());
setRemotePacketSize(buf.getInt());
byte[] addr = buf.getString();
int port = buf.getInt();
byte[] orgaddr = buf.getString();
int orgport = buf.getInt();
/*
* System.err.println("addr: "+Util.byte2str(addr)); System.err.println("port: "+port);
* System.err.println("orgaddr: "+Util.byte2str(orgaddr));
* System.err.println("orgport: "+orgport);
*/
Session _session = null;
try {
_session = getSession();
} catch (JSchException e) {
// session has been already down.
}
this.config = getPort(_session, Util.byte2str(addr), port);
if (this.config == null)
this.config = getPort(_session, null, port);
if (this.config == null) {
if (_session.getLogger().isEnabled(Logger.ERROR)) {
_session.getLogger().log(Logger.ERROR,
"ChannelForwardedTCPIP: " + Util.byte2str(addr) + ":" + port + " is not registered.");
}
}
}
private static Config getPort(Session session, String address_to_bind, int rport) {
synchronized (pool) {
for (int i = 0; i < pool.size(); i++) {
Config bar = pool.elementAt(i);
if (bar.session != session)
continue;
if (bar.rport != rport) {
if (bar.rport != 0 || bar.allocated_rport != rport)
continue;
}
if (address_to_bind != null && !bar.address_to_bind.equals(address_to_bind))
continue;
return bar;
}
return null;
}
}
static String[] getPortForwarding(Session session) {
Vector<String> foo = new Vector<>();
synchronized (pool) {
for (int i = 0; i < pool.size(); i++) {
Config config = pool.elementAt(i);
if (config.session == session) {
if (config instanceof ConfigDaemon)
foo.addElement(config.allocated_rport + ":" + config.target + ":");
else
foo.addElement(
config.allocated_rport + ":" + config.target + ":" + ((ConfigLHost) config).lport);
}
}
}
String[] bar = new String[foo.size()];
for (int i = 0; i < foo.size(); i++) {
bar[i] = foo.elementAt(i);
}
return bar;
}
static String normalize(String address) {
if (address == null) {
return "localhost";
} else if (address.length() == 0 || address.equals("*")) {
return "";
} else {
return address;
}
}
static void addPort(Session session, String _address_to_bind, int port, int allocated_port,
String target, int lport, SocketFactory factory) throws JSchException {
String address_to_bind = normalize(_address_to_bind);
synchronized (pool) {
if (getPort(session, address_to_bind, port) != null) {
throw new JSchException("PortForwardingR: remote port " + port + " is already registered.");
}
ConfigLHost config = new ConfigLHost();
config.session = session;
config.rport = port;
config.allocated_rport = allocated_port;
config.target = target;
config.lport = lport;
config.address_to_bind = address_to_bind;
config.factory = factory;
pool.addElement(config);
}
}
static void addPort(Session session, String _address_to_bind, int port, int allocated_port,
String daemon, Object[] arg) throws JSchException {
String address_to_bind = normalize(_address_to_bind);
synchronized (pool) {
if (getPort(session, address_to_bind, port) != null) {
throw new JSchException("PortForwardingR: remote port " + port + " is already registered.");
}
ConfigDaemon config = new ConfigDaemon();
config.session = session;
config.rport = port;
config.allocated_rport = port;
config.target = daemon;
config.arg = arg;
config.address_to_bind = address_to_bind;
pool.addElement(config);
}
}
static void delPort(ChannelForwardedTCPIP c) {
Session _session = null;
try {
_session = c.getSession();
} catch (JSchException e) {
// session has been already down.
}
if (_session != null && c.config != null)
delPort(_session, c.config.rport);
}
static void delPort(Session session, int rport) {
delPort(session, null, rport);
}
static void delPort(Session session, String address_to_bind, int rport) {
synchronized (pool) {
Config foo = getPort(session, normalize(address_to_bind), rport);
if (foo == null)
foo = getPort(session, null, rport);
if (foo == null)
return;
pool.removeElement(foo);
if (address_to_bind == null) {
address_to_bind = foo.address_to_bind;
}
if (address_to_bind == null) {
address_to_bind = "0.0.0.0";
}
}
Buffer buf = new Buffer(200); // ??
Packet packet = new Packet(buf);
try {
// byte SSH_MSG_GLOBAL_REQUEST 80
// string "cancel-tcpip-forward"
// boolean want_reply
// string address_to_bind (e.g. "127.0.0.1")
// uint32 port number to bind
packet.reset();
buf.putByte((byte) 80 /* SSH_MSG_GLOBAL_REQUEST */);
buf.putString(Util.str2byte("cancel-tcpip-forward"));
buf.putByte((byte) 0);
buf.putString(Util.str2byte(address_to_bind));
buf.putInt(rport);
session.write(packet);
} catch (Exception e) {
// throw new JSchException(e.toString(), e);
}
}
static void delPort(Session session) {
int[] rport = null;
int count = 0;
synchronized (pool) {
rport = new int[pool.size()];
for (int i = 0; i < pool.size(); i++) {
Config config = pool.elementAt(i);
if (config.session == session) {
rport[count++] = config.rport; // ((Integer)bar[1]).intValue();
}
}
}
for (int i = 0; i < count; i++) {
delPort(session, rport[i]);
}
}
public int getRemotePort() {
return (config != null ? config.rport : 0);
}
private void setSocketFactory(SocketFactory factory) {
if (config != null && (config instanceof ConfigLHost))
((ConfigLHost) config).factory = factory;
}
abstract static class Config {
Session session;
int rport;
int allocated_rport;
String address_to_bind;
String target;
}
static class ConfigDaemon extends Config {
Object[] arg;
}
static class ConfigLHost extends Config {
int lport;
SocketFactory factory;
}
}