ssh2connection.java
来自「一个非常好的ssh客户端实现」· Java 代码 · 共 939 行 · 第 1/2 页
JAVA
939 行
/****************************************************************************** * * Copyright (c) 1999-2003 AppGate Network Security AB. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code as * defined in and that are subject to the MindTerm Public Source License, * Version 2.0, (the 'License'). You may not use this file except in compliance * with the License. * * You should have received a copy of the MindTerm Public Source License * along with this software; see the file LICENSE. If not, write to * AppGate Network Security AB, Otterhallegatan 2, SE-41118 Goteborg, SWEDEN * *****************************************************************************/package com.mindbright.ssh2;import java.io.IOException;import java.net.Socket;import java.util.Hashtable;import java.util.Vector;import java.util.Enumeration;import com.mindbright.jca.security.SecureRandom;import com.mindbright.util.SecureRandomAndPad;import com.mindbright.util.Log;/** * This class implements the connection layer of the secure shell version 2 * (ssh2) protocol stack. This layer contains the multiplexing of the secure * connection to the server into several different channels which can be used * for port forwarding and terminal sessions as defined in the connection * protocol spec. * <p> * To create a <code>SSH2Connection</code> instance a connected * <code>SSH2Transport</code> along with an authenticated * <code>SSH2UserAuth</code> must be created first to be passed to the * constructor. Optionally a <code>SSH2ConnectionEventHandler</code> can be * supplied to be able to monitor and control the connection layer. A log * handler can also be set, though by default the connection layer uses the same * log handler as the transport layer. All preferences are taken from the * <code>SSH2Preferences</code> set in the transport layer. * <p> * The connection layer must be hooked into the transport layer explicitly by * calling the method <code>setConnection</code> on the * <code>SSH2Transport</code>. Once the connection layer is hooked up to the * transport layer channels can be created. There are basically four types of * channels: session, local forward, remote forward, and X11 forward. There are * methods for creating local and remote forwards aswell as session channels, * however, X11 channels must be created through session channels. * * @see SSH2Transport * @see SSH2UserAuth * @see SSH2ConnectionEventHandler * @see SSH2Preferences * @see SSH2Channel * @see SSH2Connector * @see SSH2Listener */public final class SSH2Connection implements Runnable { public final static int MAX_ACTIVE_CHANNELS = 1024; public final static String GL_REQ_START_FORWARD = "tcpip-forward"; public final static String GL_REQ_CANCEL_FORWARD = "cancel-tcpip-forward"; public final static String CH_REQ_PTY = "pty-req"; public final static String CH_REQ_X11 = "x11-req"; public final static String CH_REQ_ENV = "env"; public final static String CH_REQ_SHELL = "shell"; public final static String CH_REQ_EXEC = "exec"; public final static String CH_REQ_SUBSYSTEM = "subsystem"; public final static String CH_REQ_WINCH = "window-change"; public final static String CH_REQ_XONOFF = "xon-xoff"; public final static String CH_REQ_SIGNAL = "signal"; public final static String CH_REQ_EXIT_STAT = "exit-status"; public final static String CH_REQ_EXIT_SIG = "exit-signal"; public final static String CH_REQ_AUTH_AGENT = "auth-agent-req"; public final static String CH_REQ_AUTH_AGENT1 = "auth-ssh1-agent-req"; // draft-ietf-secsh-break public final static String CH_REQ_BREAK = "break"; // old OpenSSH keepalive request public final static String CH_REQ_OPENSSH_KEEPALIVE = "keepalive@openssh.com"; public final static String CHAN_FORWARDED_TCPIP = "forwarded-tcpip"; public final static String CHAN_DIRECT_TCPIP = "direct-tcpip"; public final static String CHAN_SESSION = "session"; public final static String CHAN_X11 = "x11"; public final static String CHAN_AUTH_AGENT = "auth-agent"; public final static int CH_TYPE_FWD_TCPIP = 0; public final static int CH_TYPE_DIR_TCPIP = 1; public final static int CH_TYPE_SESSION = 2; public final static int CH_TYPE_X11 = 3; public final static int CH_TYPE_AUTH_AGENT = 4; final static String[] channelTypes = { CHAN_FORWARDED_TCPIP, CHAN_DIRECT_TCPIP, CHAN_SESSION, CHAN_X11, CHAN_AUTH_AGENT }; private SSH2Transport transport; private SSH2UserAuth userAuth; private SSH2ConnectionEventHandler eventHandler; private SSH2Channel[] channels; private int totalChannels; private int nextEmptyChan; private SSH2Connector connector; private Hashtable remoteForwards; private Hashtable remoteFilters; private Hashtable localForwards; private byte[] x11RealCookie; private byte[] x11FakeCookie; private boolean x11Single; private int x11Mappings; private Object reqMonitor; private boolean reqStatus; private Log connLog; /** * Basic constructor used when there is no need for event handler. * * @param userAuth the authentication layer * @param transport the transport layer */ public SSH2Connection(SSH2UserAuth userAuth, SSH2Transport transport) { this(userAuth, transport, null); } /** * Constructor used when there need for event handler. * * @param userAuth the authentication layer * @param transport the transport layer * @param eventHandler the event handler (may be <code>null</code>) * * @see SSH2ConnectionEventHandler */ public SSH2Connection(SSH2UserAuth userAuth, SSH2Transport transport, SSH2ConnectionEventHandler eventHandler) { this.userAuth = userAuth; this.transport = transport; this.eventHandler = (eventHandler != null ? eventHandler : new SSH2ConnectionEventAdapter()); this.channels = new SSH2Channel[64]; this.totalChannels = 0; this.nextEmptyChan = 0; this.remoteForwards = new Hashtable(); this.remoteFilters = new Hashtable(); this.localForwards = new Hashtable(); this.x11RealCookie = null; this.x11FakeCookie = null; this.x11Single = false; this.x11Mappings = 0; this.connLog = transport.getLog(); this.reqMonitor = new Object(); if(transport.incompatibleBuggyChannelClose) { startChannelReaper(); } } void processGlobalMessage(SSH2TransportPDU pdu) { switch(pdu.pktType) { case SSH2.MSG_GLOBAL_REQUEST: String type = pdu.readJavaString(); boolean wantReply = pdu.readBoolean(); if(type.equals(GL_REQ_START_FORWARD)) { } else if(type.equals(GL_REQ_CANCEL_FORWARD)) { } else { } break; case SSH2.MSG_REQUEST_SUCCESS: synchronized(reqMonitor) { reqStatus = true; reqMonitor.notify(); } break; case SSH2.MSG_REQUEST_FAILURE: synchronized(reqMonitor) { reqStatus = false; reqMonitor.notify(); } break; case SSH2.MSG_CHANNEL_OPEN: getConnector().channelOpen(pdu); pdu = null; break; } if(pdu != null) { pdu.release(); } } void processChannelMessage(SSH2TransportPDU pdu) { int channelId = pdu.readInt(); SSH2Channel channel = channels[channelId]; if(channel == null) { String msg = "Error, received message to non-existent channel"; connLog.error("SSH2Connection", "processChannelMessage", msg); connLog.debug2("SSH2Connection", "processChannelMessage", "got message of type: " + SSH2.msgTypeString(pdu.pktType), pdu.getData(), pdu.getPayloadOffset(), pdu.getPayloadLength()); fatalDisconnect(SSH2.DISCONNECT_PROTOCOL_ERROR, msg); return; } switch(pdu.pktType) { case SSH2.MSG_CHANNEL_OPEN_CONFIRMATION: channel.openConfirmation(pdu); break; case SSH2.MSG_CHANNEL_OPEN_FAILURE: int reasonCode = pdu.readInt(); String reasonText; String langTag; if(transport.incompatibleChannelOpenFail) { reasonText = ""; langTag = ""; } else { reasonText = pdu.readJavaString(); langTag = pdu.readJavaString(); } channel.openFailure(reasonCode, reasonText, langTag); break; case SSH2.MSG_CHANNEL_WINDOW_ADJUST: channel.windowAdjust(pdu); break; case SSH2.MSG_CHANNEL_DATA: channel.data(pdu); pdu = null; break; case SSH2.MSG_CHANNEL_EXTENDED_DATA: channel.extData(pdu); pdu = null; break; case SSH2.MSG_CHANNEL_EOF: channel.recvEOF(); break; case SSH2.MSG_CHANNEL_CLOSE: channel.recvClose(); break; case SSH2.MSG_CHANNEL_REQUEST: channel.handleRequest(pdu); break; case SSH2.MSG_CHANNEL_SUCCESS: channel.requestSuccess(pdu); break; case SSH2.MSG_CHANNEL_FAILURE: channel.requestFailure(pdu); break; } if(pdu != null) { pdu.release(); } } /** * Gets our transport layer. * * @return the transport layer */ public SSH2Transport getTransport() { return transport; } /** * Sets the event handler to use. * * @param eventHandler the event handler to use */ public void setEventHandler(SSH2ConnectionEventHandler eventHandler) { if(eventHandler != null) { this.eventHandler = eventHandler; } } /** * Gets the event handler currently in use. * * @return the event handler currently in use */ public SSH2ConnectionEventHandler getEventHandler() { return eventHandler; } /** * Gets the preferences set in the transport layer. * * @return the preferences */ public SSH2Preferences getPreferences() { return transport.getOurPreferences(); } /** * Gets the log handler currently in use. * * @return the log handler currently in use */ public Log getLog() { return connLog; } /** * Sets the log handler to use. * * @param the log handler to use */ public void setLog(Log log) { connLog = log; } /** * Gets the <code>SecureRandom</code> currently in use (i.e. from the * transport layer). * * @return the <code>SecureRandom</code> in use */ public SecureRandom getSecureRandom() { return transport.getSecureRandom(); } /** * Transmits the given PDU (by sending it to the transport layer, no * processing is needed at this point). * * @param pdu packet to send */ public void transmit(SSH2TransportPDU pdu) { transport.transmit(pdu); } /** * Disconnects from peer using the DISCONNECT packet type with the given * reason and description. See the class <code>SSH2</code> for reason codes. * This is only a convenience method which calls the same method on the * transport layer. * * @param reason the reason code * @param description the textual description for the cause of disconnect * * @see SSH2 */ public void fatalDisconnect(int reason, String description) { transport.fatalDisconnect(reason, description); } /** * Gets the singleton instance of the <code>SSH2Connector</code> which is * used by the connection layer to connect remote forwards through to local * hosts when they are opened. * * @return the singleton connector */ public SSH2Connector getConnector() { if(connector == null) { connector = new SSH2Connector(this); } return connector; } /** * Gets the local target host address and port pair of a remote forward * identified by the given remote address and port pair. This function is * used to locate the local target of a remote forward when it is opened. * * @param remoteAddr the remote address of the forward * @param remotePort the remote port of the forward * * @return the address and port, the address beeing at index 0 and the port * beeing at inde 1. */ public synchronized String[] getForwardTarget(String remoteAddr, int remotePort) { String[] target = null; String tgStr = (String)remoteForwards.get(remoteAddr + ":" + remotePort); if(tgStr != null) { target = new String[2]; int i = tgStr.indexOf(":"); target[0] = tgStr.substring(0, i); target[1] = tgStr.substring(i + 1); } return target; } /** * Gets the filter factory instance for a remote forward identified by the * given remote address and port pair. * * @param remoteAddr the remote address of the forward * @param remotePort the remote port of the forward * * @return the stream filter factory instance */ public synchronized SSH2StreamFilterFactory getForwardFilterFactory(String remoteAddr, int remotePort) { return (SSH2StreamFilterFactory)remoteFilters.get(remoteAddr + ":" + remotePort); } /** * Gets the <code>SSH2Listener</code> instance of a local forward if it is * set up. * * @param localAddr the local address of the forward * @param locaPort the local port of the forward * * @return the listener instance for the given forward or <code>null</code> * if none is set */ public synchronized SSH2Listener getLocalListener(String localAddr, int localPort) { return (SSH2Listener)localForwards.get(localAddr + ":" + localPort); } /** * Creates a new remote forward from the given remote address and port on * the server to the local address and port. * * @param remoteAddr the remote address where the server listens * @param remotePort the remote port where the server listens * @param localAddr the local address to connect through to * @param localAddr the local port to connect through to */ public synchronized void newRemoteForward(String remoteAddr, int remotePort, String localAddr, int localPort) { newRemoteForward(remoteAddr, remotePort, localAddr, localPort, (SSH2StreamFilterFactory)null);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?