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 + -
显示快捷键?