This is an automated email from the git hooks/post-receive script. ebourg-guest pushed a commit to branch master-old in repository trilead-ssh2.
commit a3c2b7d404149a7588f64c1840cdb7e9e301d408 Author: Matthew Johnson <[email protected]> Date: Sat Feb 20 14:14:59 2010 +0000 new upstream --- debian/FAQ.html | 389 +++++++++++++ debian/README.txt | 24 + debian/changelog | 9 + debian/control | 2 +- debian/copyright | 3 +- debian/examples/Basic.java | 91 +++ debian/examples/BasicWithHTTPProxy.java | 102 ++++ debian/examples/PortForwarding.java | 116 ++++ debian/examples/PublicKeyAuthentication.java | 80 +++ debian/examples/SimpleVerifier.java | 55 ++ debian/examples/SingleThreadStdoutStderr.java | 142 +++++ debian/examples/StdoutAndStderr.java | 93 +++ debian/examples/SwingShell.java | 786 ++++++++++++++++++++++++++ debian/examples/UsingKnownHosts.java | 86 +++ debian/libtrilead-ssh2-java.docs | 6 +- debian/rules | 3 - 16 files changed, 1979 insertions(+), 8 deletions(-) diff --git a/debian/FAQ.html b/debian/FAQ.html new file mode 100644 index 0000000..979a6c3 --- /dev/null +++ b/debian/FAQ.html @@ -0,0 +1,389 @@ +<html> +<title>Trilead SSH-2 for Java FAQ</title> +<body> + +<a name="oben"></a> +<h1>Trilead SSH-2 for Java FAQ</h1> + +<p> +This FAQ includes information regarding topics that were discussed in e-mails between developers and users +of the Trilead SSH-2 for Java library. +</p> +<p> +Trilead homepage: <a href="http://www.trilead.ethz.ch">http://www.trilead.ethz.ch</a><br> +Last update of FAQ: oct-15-2007. +</p> +<p> +Please report bugs, typos and any kind of suggestions to [email protected]. +Also, please visit our <a href="http://www.trilead.com/support">support forum</a>. +</p> + +<hr> + +<h2>Sections:</h2> + +<p> +<ul> +<li><a href="#env">When I start program XYZ with putty (or openssh, ..., whatever) then everything works. +However, if I use "Session.execCommand", then XYZ behaves differently or does not work at all!</a></li> + +<li><a href="#blocking">My program sometimes hangs when I only read output from stdout! +Or: can you explain me the story about the shared stdout/stderr window in the SSH-2 protocol? +Or: what is this "StreamGobbler" thing all about?</a></li> + +<li><a href="#buffered">Why are the session's Input- and OutputStreams not buffered?</a></li> + +<li><a href="#sessioncommands">Why can't I execute several commands in one single session?</a></li> + +<li><a href="#sessionlimit">I cannot open more than 10 concurrent sessions (or SCP clients).</a></li> + +<li><a href="#passwordauth">Password authentication fails, I get "Authentication method password not +supported by the server at this stage".</a></li> + +<li><a href="#puttygen">Why does public key authentication fail with my putty key?</a></li> + +<li><a href="#catmethod">I am sending data to a remote file using the "cat" method, but not all data is being written.</a></li> + +<li><a href="#pumptoremote">I want to pump data into a remote file, but the amount of data to be sent +is not known at the time the transfer starts.</a></li> + +<li><a href="#swingshell">Do you have an example for the usage of feature XYZ?</a></li> +</ul> +</p> + +<hr><a name="env"></a><h2>When I start program XYZ with putty (or openssh, ..., whatever) then everything +works. However, if I use "Session.execCommand", then XYZ behaves differently or does not work at all!</h2> + +<h3>Short answer:</h3> + +<p> +The most often source of problems when executing a command with <tt>Session.execCommand()</tt> +are missing/wrong set environment variables on the remote machine. Make sure that the minimum needed +environment for XYZ is the same, independentely on how the shell is being invoked. +</p> + +<p> +Example quickfix for bash users: +</p> + +<p> +<ol> +<li>Define all your settings in the file <tt><b>~/.bashrc</b></tt></li> +<li>Make sure that the file <tt><b>~/.bash_profile</b></tt> only contains the line <tt><b>source +~/.bashrc</b></tt>.</li> +<li>Before executing <tt>Session.execCommand()</tt>, do NOT aquire any type of pseudo terminal in the +session. Be prepared to consume stdout and stderr data.</li> +</ol> +</p> + +<p> +<b>Note:</b> If you really want to mimic the behavior of putty, then don't use Session.execCommand(), +instead aquire a pty (pseudo terminal) and then start a shell (use <tt>Session.requestPTY()</tt> and +<tt>Session.startShell()</tt>). You then have to communicate with the shell process at the other end +through stdin and stdout. However, you also have to implement terminal logic (e.g., escape sequence +handling (unless you use a "dumb" pty), "expect-send" logic (output parsing, shell prompt detection), etc.). +</p> + +<h3>Long answer:</h3> + +<p> +If you login by using putty, then putty will normally request a "xterm" pty and your assigned shell +(e.g., bash) will be started (a so called "interactive login shell"). In contrast, if you use +<tt>Session.execCommand()</tt> to start a command then (unless you ask for it) no pty will be aquired +and the command will be given to the shell as an argument (with the shell's "-c" option). +</p> + +<p> +The way a shell is being invoked has an effect on the set of initialization files which will be read be the shell. +</p> + +<p> +To demonstrate the difference, try the following (from the command line, e.g., with an OpenSSH client): +</p> + +<p> +<ol> +<li>Login interactively and print the environment with the "env" command:<br> <br> +<tt><b>[user@host ~] ssh 127.0.0.1<br> +[user@host ~] env</b></tt><br> <br> +</li> +<li>Let the ssh server execute the "env" command (equivalent to using <tt>Session.executeCommand()</tt>):<br> <br> +<tt><b>[user@host ~] ssh 127.0.0.1 "env"</b></tt> +</li> +</ol> +</p> + +<p> +If you compare the two outputs, then you will (unless you have adjusted your shell's settings) +observe different environments. +</p> + +<p> +<b>If you are interested in the details, then please read the <tt>INVOCATION</tt> section in man page +for the bash shell. You may notice that the definitions of "interactive" and "non-interactive" +(and combinations with "login") are little bit tricky.</b> +</p> + +[<a href="#oben">TOP</a>] + +<hr><a name="blocking"></a><h2>My program sometimes hangs when I only read output from stdout! +Or: can you explain me the story about the shared stdout/stderr window in the SSH-2 protocol? +Or: what is this "StreamGobbler" thing all about?</h2> + +<p> +In the SSH-2 low level protocol, each channel (e.g., session) has a receive window. When the remote +SSH daemon has filled up our receive window, it must wait until we have consumed the input and are ready to accept new data. +</p> + +<p> +Unfortunately, the SSH-2 protocol defines a shared window for stderr and stdout. As a consequence, +if, for example, the remote process produces a lot of stderr data and you never consume it, then after +some time the local receive window will be full and the sender is blocked. If you then try to read() +from stdout, your call will be blocked: there is no stdout data (locally) available and the SSH daemon +cannot send you any, since the receive window is full (you would have to read some stderr data first +to "free" up space in the receive window). +</p> + +<p> +Fortunately, Trilead SSH-2 uses a 30KB window - the above described scenario should be very rare. +</p> + +<p> +Many other SSH-2 client implementations just blindly consume any remotely produced data into a buffer +which gets automatically extended - however, this can lead to another problem: in the extreme case +the remote side can overflow you with data (e.g., leading to out of memory errors). +</p> + +<p> +What can you do about this? +</p> + +<p> +<ol> +<li><b>Bad: Do nothing</b> - just work with stderr and stdout Inputstreams and hope that the 30KB +window is enough for your application.</li> + +<li><b>Better, recommended for most users:</b> use two worker threads that consume remote stdout +and stderr in parallel. Since you probably are not in the mood to program such a thing, you can use +the StreamGobbler class supplied with Trilead SSH-2. The Streamgobbler is a special InputStream that +uses an internal worker thread to read and buffer internally all data produced by another InputStream. +It is very simple to use:<br> <tt><b><pre>InputStream stdout = new StreamGobbler(mysession.getStdout()); + +InputStream stderr = new StreamGobbler(mysession.getStderr());</pre></b></tt> +You then can access stdout and stderr in any order, in the background the StreamGobblers will +automatically consume all data from the remote side and store in an internal buffer.</li> + +<li><b>Advanced:</b> you are paranoid and don't like programs that automatically extend buffers +without asking you. You then have to implement a state machine. The condition wait facility offered by +<tt>Session.waitForCondition()</tt> is exactly what you need: you can use it to wait until either stdout +or stderr data has arrived and can be consumed with the two InputStreams. You can either use the return value +of <tt>Session.waitForCondition()</tt> or check with <tt>InputStream.available()</tt> +(for stdout and stderr) which InputStream has data available (i.e., a <tt>read()</tt> call will not block). +Be careful when wrapping the InputStreams, also do not concurrently call read() on the InputStreams while calling +<tt>Session.waitForCondition()</tt> (unless you know what you are doing).<br>Please have a look a the +<tt>SingleThreadStdoutStderr.java</tt> example.</li> + +<li><b>The lazy way:</b> you don't mind if stdout and stderr data is being mixed into the same +stream. Just allocate a "dumb" pty and the server will hopefully not send you any data on the stderr +stream anymore. <b>Note:</b> by allocating a pty, the shell used to execute the command will probably +behave differently in terms of initialization (see also <a href="#env">this question</a>).</li> +</ol> +</p> + + +[<a href="#oben">TOP</a>] + +<hr><a name="buffered"></a><h2>Why are the session's Input- and OutputStreams not buffered?</h2> + +<p> +If you need it, then this library offers quite a raw type of access to the SSH-2 protocol stack. +Of course, many people don't need that kind of low level access. If you need buffered streams, +then you should the do the same thing as you would probably do with the streams of a TCP socket: +wrap them with instances of BufferedInputStream and BufferedOutputStream. In case you use +StreamGobblers for the InputStreams, then you don't need any additional wrappers, since the +StreamGobblers implement buffering already. +</p> +<p> +This code snippet will probably work well for most people: +</p> +<p> +<tt> +<pre> +InputStream stdout = new StreamGobbler(mysession.getStdout()); +InputStream stderr = new StreamGobbler(mysession.getStderr()); +OutputStream stdin = new BufferedOutputStream(mysession.getStdin(), 8192); +</pre> +</tt> +</p> + +[<a href="#oben">TOP</a>] + +<hr><a name="sessioncommands"></a><h2>Why can't I execute several commands in one single session?</h2> +<p> +If you use <tt>Session.execCommand()</tt>, then you indeed can only execute only one command per session. +This is not a restriction of the library, but rather an enforcement by the underlying SSH-2 protocol +(a <tt>Session</tt> object models the underlying SSH-2 session). +</p> +<p> +There are several solutions: +</p> +<p> +<ul> +<li><b>Simple: Execute several commands in one batch</b>, e.g., something like <tt>Session.execCommand("echo +Hello && echo again")</tt>.</li> +<li><b>Simple: The intended way: simply open a new session for each command</b> - once you have opened a +connection, you can ask for as many sessions as you want, they are only a "virtual" construct.</li> +<li><b>Advanced: Don't use <tt>Session.execCommand()</tt>, but rather aquire a shell with +<tt>Session.startShell()</tt></b>. See also <a href="#env">this question</a>.</li> +</ul> +</p> + + +[<a href="#oben">TOP</a>] + +<hr><a name="sessionlimit"></a><h2>I cannot open more than 10 concurrent sessions (or SCP clients).</h2> +<p> +You are probably using OpenSSH. By looking at their source code you will find out that there +is a hard-coded constant called MAX_SESSIONS in the session.c file which is set to "10" by default. +This is a per connection limit. Unfortunately, it is not a run-time tunable parameter. +However, this limit has no effect on the number of concurrent port forwardings. Please note: this information +is based on the OpenSSH 4.3 release. +</p> +<p> +Possible solutions: +<ul> +<li>(a) Recompile your SSH daemon</li> +<li>(b) Try to live with this limit and keep the number of concurrent sessions <= 10.</li> +<li>(c) Distribute your sessions over multiple concurrent SSH connections.</li> +</ul> +</p> +<p> +Just for completeness: starting from release 210, the thrown exception may look as follows:<br> +<tt> +<pre> +java.io.IOException: Could not open channel (The server refused to open the channel (SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, 'open failed')) +</pre> +</tt> +</p> + +[<a href="#oben">TOP</a>] + +<hr><a name="passwordauth"></a><h2>Password authentication fails, I get "Authentication method password +not supported by the server at this stage".</h2> + +<p> +Many default SSH server installations are configured to refuse the authentication type "password". +Often, they only accept "publickey" and "keyboard-interactive". You have different options: +</p> + +<p> +<ul> +<li><b>Enable password authentication.</b> E.g., in case of OpenSSH on Fedora, edit +<code>/etc/sshd/sshd_config</code> and change the value of "PasswordAuthentication" to "yes", +then send a HUP signal to the daemon so that it re-reads its configuration.</li> +<li><b>Switch to public-key authentication.</b> Probably the best choice.</li> +<li><b>Try to use keyboard-interactive authentication.</b> If you have a GUI that interacts with a user, +then this is doable (check out the SwingShell.java example).</li> +</ul> +</p> + +<p> +In general it is a good idea to call either <code>Connection.getRemainingAuthMethods()</code> +or <code>Connection.isAuthMethodAvailable()</code> before using a certain authentication method. +</p> + +<p> +Please note that most servers let you in after one successful authentication step. However, in rare cases +you may encounter servers that need several steps. I.e., if one of the <code>Connection.authenticateWithXXX()</code> +methods returns <code>false</code> and <code>Connection.isAuthenticationPartialSuccess()</code> returns +<code>true</code>, then further authentication is needed. For each step, to find out which authentication methods +may proceed, you can use either the <code>Connection.getRemainingAuthMethods()</code> +or the <code>Connection.isAuthMethodAvailable()</code> method. Again, please have a look into the +SwingShell.java example. +</p> + +[<a href="#oben">TOP</a>] + +<hr><a name="puttygen"></a><h2>Why does public key authentication fail with my putty key?</h2> +<p> +When using putty private keys (e.g., .ppk files) with public key authentication, you get a +"Publickey authentication failed" exception. The reason is that the library currently is not able to +directly handle private keys in the proprietary format used by putty. However, you can use the +"puttygen" tool (from the putty website) to convert your key to the desired format: load your key, +then go to the conversions menu and select "Save OpenSSH key" (which saves the key in openssl PEM format, +e.g., call it "private.pem"). +</p> + +[<a href="#oben">TOP</a>] + +<hr><a name="catmethod"></a><h2>I am sending data to a remote file using the "cat" method, but not all data is being written.</h2> +<p> +Please read carefully the answer to the following <a href="#pumptoremote">question</a>. +</p> + +[<a href="#oben">TOP</a>] + + +<hr><a name="pumptoremote"></a><h2>I want to pump data into a remote file, but the amount of data to be sent +is not known at the time the transfer starts.</h2> +<p> +The SCP protocol communicates the amount of data to be sent at the start of the transfer, +so SCP remains out of consideration. Possible other solutions: +<ul> +<li>Use the SFTP client. Recommended.</li> +<li>Execute "cat > filename.txt" on the remote side and pump the data into stdin. This method is NOT recommended (and won't work on Windows...).</li> +</ul> +</p> +<p> +Be careful if you use the "cat" approach, as it may happen that not all your data will be +written. If you close the stdin stream and immediatelly close the session (or the whole connection) then +some SSH servers do not send the pending data to the process being executed ("cat" in this case). +You have to wait until "cat" has received the EOF and terminates before closing the session. However, +waiting for the termination may not always work, since SSH servers sometimes "forget" to send the exit code +of the remote process. The following code MAY work: +</p> +<p> +<tt> +<pre> +Session sess = conn.openSession(); +sess.execCommand("cat > test.txt"); +OutputStream stdin = sess.getStdin(); + +... out.write(...) ... out.write(...) ... + +/* The following flush() is only needed if you wrap the */ +/* stdin stream (e.g., with a BufferedOutputStream). */ +out.flush(); + +/* Now let's send EOF */ +out.close(); + +/* Let's wait until cat has finished */ +sess.waitForCondition(ChannelCondition.EXIT_STATUS, 2000); +/* Better: put the above statement into a while loop! */ +/* In ANY CASE: read the Javadocs for waitForCondition() */ + +/* Show exit status, if available (otherwise "null") */ +System.out.println("ExitCode: " + sess.getExitStatus()); +/* Now its hopefully safe to close the session */ +sess.close(); +</pre> +</tt> +</p> +<p> +(Just a thought for another solution: execute <code>cat > test.txt && echo "FINISHED"</code> +and wait until you get "FINISHED" on stdout... - try it on your own risk =) +</p> + +[<a href="#oben">TOP</a>] + +<hr><a name="swingshell"></a><h2>Do you have an example for the usage of feature XYZ?</h2> +<p> +Please have at look at the examples section in the distribution, especially at the SwingShell.java example. +</p> + +[<a href="#oben">TOP</a>] + +</body> +</html> + diff --git a/debian/README.txt b/debian/README.txt new file mode 100644 index 0000000..69bee5a --- /dev/null +++ b/debian/README.txt @@ -0,0 +1,24 @@ + +Trilead SSH-2 for Java - build 211 +================================== + +http://www.trilead.com + +Trilead SSH-2 for Java is a library which implements the SSH-2 protocol in pure Java +(minimum required JRE: 1.4.2). It allows one to connect to SSH servers from within +Java programs. It supports SSH sessions (remote command execution and shell access), +local and remote port forwarding, local stream forwarding, X11 forwarding, SCP and SFTP. +There are no dependencies on any JCE provider, as all crypto functionality is included. + +This distribution contains the source code, examples, javadoc and the FAQ. +It also includes a pre-compiled jar version of the library which is ready to use. + +- Please read the included LICENCE.txt +- Latest changes can be found in HISTORY.txt + +The latest version of the FAQ is available on the website. + +Please feel free to contact us. We welcome feedback of any kind! +Contact: [email protected] or go to the public forum at http://www.trilead.com + +Zurich, October 2007 diff --git a/debian/changelog b/debian/changelog index 11745d2..1d65963 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +trilead-ssh2 (6401-1) unstable; urgency=low + + * Switch upstream to svnkit + * New upstream release + * Version is svn revision from svnkit + * Import readme, faq and examples from old upstream + + -- Matthew Johnson <[email protected]> Sat, 20 Feb 2010 13:52:54 +0000 + trilead-ssh2 (211-3) unstable; urgency=low * Use javahelper rather than dh_javadoc diff --git a/debian/control b/debian/control index abc6885..a1ae852 100644 --- a/debian/control +++ b/debian/control @@ -5,7 +5,7 @@ Maintainer: Matthew Johnson <[email protected]> Build-Depends: debhelper (>> 7), javahelper (>=0.25) Build-Depends-Indep: default-jdk Standards-Version: 3.7.3 -Homepage: http://www.trilead.com/Products/Trilead-SSH-2-Java/ +Homepage: http://svn.svnkit.com/repos/svnkit/tags/1.3.2/contrib/trilead/ Package: libtrilead-ssh2-java Architecture: all diff --git a/debian/copyright b/debian/copyright index 6c8ee89..2f12e07 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,6 +1,7 @@ This package was Debianised by Matthew Johnson <[email protected]> on Wed Feb 20 11:41:05 GMT 2008 -This package was downloaded from http://www.trilead.com/Download/Trilead-SSH-2-Java/. +This package was originally downloaded from http://www.trilead.com/Download/Trilead-SSH-2-Java/, +but now upstream is http://svn.svnkit.com/repos/svnkit/tags/1.3.2/contrib/trilead/ Copyright (c) 2007 Trilead AG (http://www.trilead.com) diff --git a/debian/examples/Basic.java b/debian/examples/Basic.java new file mode 100644 index 0000000..7bd1577 --- /dev/null +++ b/debian/examples/Basic.java @@ -0,0 +1,91 @@ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import com.trilead.ssh2.Connection; +import com.trilead.ssh2.Session; +import com.trilead.ssh2.StreamGobbler; + + +/** + * This is a very basic example that shows + * how one can login to a machine and execute a command. + * + * @author Christian Plattner, [email protected] + * @version $Id: Basic.java,v 1.4 2007/10/15 12:49:57 cplattne Exp $ + */ +public class Basic +{ + public static void main(String[] args) + { + String hostname = "127.0.0.1"; + String username = "joe"; + String password = "joespass"; + + try + { + /* Create a connection instance */ + + Connection conn = new Connection(hostname); + + /* Now connect */ + + conn.connect(); + + /* Authenticate. + * If you get an IOException saying something like + * "Authentication method password not supported by the server at this stage." + * then please check the FAQ. + */ + + boolean isAuthenticated = conn.authenticateWithPassword(username, password); + + if (isAuthenticated == false) + throw new IOException("Authentication failed."); + + /* Create a session */ + + Session sess = conn.openSession(); + + sess.execCommand("uname -a && date && uptime && who"); + + System.out.println("Here is some information about the remote host:"); + + /* + * This basic example does not handle stderr, which is sometimes dangerous + * (please read the FAQ). + */ + + InputStream stdout = new StreamGobbler(sess.getStdout()); + + BufferedReader br = new BufferedReader(new InputStreamReader(stdout)); + + while (true) + { + String line = br.readLine(); + if (line == null) + break; + System.out.println(line); + } + + /* Show exit status, if available (otherwise "null") */ + + System.out.println("ExitCode: " + sess.getExitStatus()); + + /* Close this session */ + + sess.close(); + + /* Close the connection */ + + conn.close(); + + } + catch (IOException e) + { + e.printStackTrace(System.err); + System.exit(2); + } + } +} diff --git a/debian/examples/BasicWithHTTPProxy.java b/debian/examples/BasicWithHTTPProxy.java new file mode 100644 index 0000000..73492db --- /dev/null +++ b/debian/examples/BasicWithHTTPProxy.java @@ -0,0 +1,102 @@ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import com.trilead.ssh2.Connection; +import com.trilead.ssh2.HTTPProxyData; +import com.trilead.ssh2.Session; +import com.trilead.ssh2.StreamGobbler; + +/** + * This is a very basic example that shows + * how one can login to a machine (via a HTTP proxy) + * and execute a command. + * + * @author Christian Plattner, [email protected] + * @version $Id: BasicWithHTTPProxy.java,v 1.3 2007/10/15 12:49:57 cplattne Exp $ + */ +public class BasicWithHTTPProxy +{ + public static void main(String[] args) + { + String hostname = "my-ssh-server"; + String username = "joe"; + String password = "joespass"; + + String proxyHost = "192.168.1.1"; + int proxyPort = 3128; // default port used by squid + + try + { + /* Create a connection instance */ + + Connection conn = new Connection(hostname); + + /* We want to connect through a HTTP proxy */ + + conn.setProxyData(new HTTPProxyData(proxyHost, proxyPort)); + + // if the proxy requires basic authentication: + // conn.setProxyData(new HTTPProxyData(proxyHost, proxyPort, "username", "secret")); + + /* Now connect (through the proxy) */ + + conn.connect(); + + /* Authenticate. + * If you get an IOException saying something like + * "Authentication method password not supported by the server at this stage." + * then please check the FAQ. + */ + + boolean isAuthenticated = conn.authenticateWithPassword(username, password); + + if (isAuthenticated == false) + throw new IOException("Authentication failed."); + + /* Create a session */ + + Session sess = conn.openSession(); + + sess.execCommand("uname -a && date && uptime && who"); + + System.out.println("Here is some information about the remote host:"); + + /* + * This basic example does not handle stderr, which is sometimes dangerous + * (please read the FAQ). + */ + + InputStream stdout = new StreamGobbler(sess.getStdout()); + + BufferedReader br = new BufferedReader(new InputStreamReader(stdout)); + + while (true) + { + String line = br.readLine(); + if (line == null) + break; + System.out.println(line); + } + + /* Show exit status, if available (otherwise "null") */ + + System.out.println("ExitCode: " + sess.getExitStatus()); + + /* Close this session */ + + sess.close(); + + /* Close the connection */ + + conn.close(); + + } + catch (IOException e) + { + e.printStackTrace(System.err); + System.exit(2); + } + } +} diff --git a/debian/examples/PortForwarding.java b/debian/examples/PortForwarding.java new file mode 100644 index 0000000..dd8d4df --- /dev/null +++ b/debian/examples/PortForwarding.java @@ -0,0 +1,116 @@ +import java.io.File; +import java.io.IOException; + +import com.trilead.ssh2.Connection; +import com.trilead.ssh2.LocalPortForwarder; + +/** + * This example shows how to deal with port forwardings. + * + * @author Christian Plattner, [email protected] + * @version $Id: PortForwarding.java,v 1.2 2007/10/15 12:49:57 cplattne Exp $ + */ +public class PortForwarding +{ + public static void sleepSomeTime(long milliSeconds) + { + try + { + Thread.sleep(milliSeconds); + } + catch (InterruptedException e) + { + } + } + + public static void main(String[] args) + { + String hostname = "127.0.0.1"; + String username = "joe"; + + File keyfile = new File("~/.ssh/id_rsa"); // or "~/.ssh/id_dsa" + String keyfilePass = "joespass"; // will be ignored if not needed + + try + { + /* Create a connection instance */ + + Connection conn = new Connection(hostname); + + /* Now connect */ + + conn.connect(); + + /* Authenticate */ + + boolean isAuthenticated = conn.authenticateWithPublicKey(username, keyfile, keyfilePass); + + if (isAuthenticated == false) + throw new IOException("Authentication failed."); + + /* ===== OK, now let's establish some local port forwardings ===== */ + + /* Example Port Forwarding: -L 8080:www.icann.org:80 (OpenSSH notation) + * + * This works by allocating a socket to listen on 8080 on the local interface (127.0.0.1). + * Whenever a connection is made to this port (127.0.0.1:8080), the connection is forwarded + * over the secure channel, and a connection is made to www.icann.org:80 from the remote + * machine (i.e., the ssh server). + * + * (the above text is based partially on the OpenSSH man page) + */ + + /* You can create as many of them as you want */ + + LocalPortForwarder lpf1 = conn.createLocalPortForwarder(8080, "www.icann.org", 80); + + /* Now simply point your webbrowser to 127.0.0.1:8080 */ + /* (on the host where you execute this program) */ + + /* ===== OK, now let's establish some remote port forwardings ===== */ + + /* Example Port Forwarding: -R 127.0.0.1:8080:www.ripe.net:80 (OpenSSH notation) + * + * Specifies that the port 127.0.0.1:8080 on the remote server is to be forwarded to the + * given host and port on the local side. This works by allocating a socket to listen to port + * 8080 on the remote side (the ssh server), and whenever a connection is made to this port, the + * connection is forwarded over the secure channel, and a connection is made to + * www.ripe.net:80 by the Trilead SSH-2 library. + * + * (the above text is based partially on the OpenSSH man page) + */ + + /* You can create as many of them as you want */ + + conn.requestRemotePortForwarding("127.0.0.1", 8080, "www.ripe.net", 80); + + /* Now, on the ssh server, if you connect to 127.0.0.1:8080, then the connection is forwarded + * through the secure tunnel to the library, which in turn will forward the connection + * to www.ripe.net:80. */ + + /* Sleep a bit... (30 seconds) */ + sleepSomeTime(30000); + + /* Stop accepting remote connections that are being forwarded to www.ripe.net:80 */ + + conn.cancelRemotePortForwarding(8080); + + /* Sleep a bit... (20 seconds) */ + sleepSomeTime(20000); + + /* Stop accepting connections on 127.0.0.1:8080 that are being forwarded to www.icann.org:80 */ + + lpf1.close(); + + /* Close the connection */ + + conn.close(); + + } + catch (IOException e) + { + e.printStackTrace(System.err); + System.exit(2); + } + } +} diff --git a/debian/examples/PublicKeyAuthentication.java b/debian/examples/PublicKeyAuthentication.java new file mode 100644 index 0000000..f3cb554 --- /dev/null +++ b/debian/examples/PublicKeyAuthentication.java @@ -0,0 +1,80 @@ +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import com.trilead.ssh2.Connection; +import com.trilead.ssh2.Session; +import com.trilead.ssh2.StreamGobbler; + +/** + * This example shows how to login using + * public key authentication. + * + * @author Christian Plattner, [email protected] + * @version $Id: PublicKeyAuthentication.java,v 1.2 2007/10/15 12:49:57 cplattne Exp $ + */ +public class PublicKeyAuthentication +{ + public static void main(String[] args) + { + String hostname = "127.0.0.1"; + String username = "joe"; + + File keyfile = new File("~/.ssh/id_rsa"); // or "~/.ssh/id_dsa" + String keyfilePass = "joespass"; // will be ignored if not needed + + try + { + /* Create a connection instance */ + + Connection conn = new Connection(hostname); + + /* Now connect */ + + conn.connect(); + + /* Authenticate */ + + boolean isAuthenticated = conn.authenticateWithPublicKey(username, keyfile, keyfilePass); + + if (isAuthenticated == false) + throw new IOException("Authentication failed."); + + /* Create a session */ + + Session sess = conn.openSession(); + + sess.execCommand("uname -a && date && uptime && who"); + + InputStream stdout = new StreamGobbler(sess.getStdout()); + + BufferedReader br = new BufferedReader(new InputStreamReader(stdout)); + + System.out.println("Here is some information about the remote host:"); + + while (true) + { + String line = br.readLine(); + if (line == null) + break; + System.out.println(line); + } + + /* Close this session */ + + sess.close(); + + /* Close the connection */ + + conn.close(); + + } + catch (IOException e) + { + e.printStackTrace(System.err); + System.exit(2); + } + } +} diff --git a/debian/examples/SimpleVerifier.java b/debian/examples/SimpleVerifier.java new file mode 100644 index 0000000..0cc9a79 --- /dev/null +++ b/debian/examples/SimpleVerifier.java @@ -0,0 +1,55 @@ +import com.trilead.ssh2.KnownHosts; +import com.trilead.ssh2.ServerHostKeyVerifier; + +/** + * This example hostkey verifier is used by the + * UsingKnownHosts.java example. + * + * @author Christian Plattner, [email protected] + * @version $Id: SimpleVerifier.java,v 1.4 2007/10/15 12:49:57 cplattne Exp $ + */ +class SimpleVerifier implements ServerHostKeyVerifier +{ + KnownHosts database; + + public SimpleVerifier(KnownHosts database) + { + if (database == null) + throw new IllegalArgumentException(); + + this.database = database; + } + + public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) + throws Exception + { + int result = database.verifyHostkey(hostname, serverHostKeyAlgorithm, serverHostKey); + + switch (result) + { + case KnownHosts.HOSTKEY_IS_OK: + + return true; // We are happy + + case KnownHosts.HOSTKEY_IS_NEW: + + // Unknown host? Blindly accept the key and put it into the cache. + // Well, you definitely can do better (e.g., ask the user). + + // The following call will ONLY put the key into the memory cache! + // To save it in a known hosts file, also call "KnownHosts.addHostkeyToFile(...)" + database.addHostkey(new String[] { hostname }, serverHostKeyAlgorithm, serverHostKey); + + return true; + + case KnownHosts.HOSTKEY_HAS_CHANGED: + + // Close the connection if the hostkey has changed. + // Better: ask user and add new key to database. + return false; + + default: + throw new IllegalStateException(); + } + } +} \ No newline at end of file diff --git a/debian/examples/SingleThreadStdoutStderr.java b/debian/examples/SingleThreadStdoutStderr.java new file mode 100644 index 0000000..5887fa6 --- /dev/null +++ b/debian/examples/SingleThreadStdoutStderr.java @@ -0,0 +1,142 @@ +import java.io.IOException; +import java.io.InputStream; + +import com.trilead.ssh2.ChannelCondition; +import com.trilead.ssh2.Connection; +import com.trilead.ssh2.Session; + +/** + * This example shows how to use the Session.waitForCondition + * method to implement a state machine approach for + * proper stdout/stderr output handling in a single thread. + * + * @author Christian Plattner, [email protected] + * @version $Id: SingleThreadStdoutStderr.java,v 1.6 2007/10/15 12:49:57 cplattne Exp $ + */ +public class SingleThreadStdoutStderr +{ + public static void main(String[] args) + { + String hostname = "127.0.0.1"; + String username = "joe"; + String password = "joespass"; + + try + { + /* Create a connection instance */ + + Connection conn = new Connection(hostname); + + /* Now connect */ + + conn.connect(); + + /* Authenticate */ + + boolean isAuthenticated = conn.authenticateWithPassword(username, password); + + if (isAuthenticated == false) + throw new IOException("Authentication failed."); + + /* Create a session */ + + Session sess = conn.openSession(); + + sess.execCommand("echo \"Huge amounts of text on STDOUT\"; echo \"Huge amounts of text on STDERR\" >&2"); + + /* + * Advanced: + * The following is a demo on how one can read from stdout and + * stderr without having to use two parallel worker threads (i.e., + * we don't use the Streamgobblers here) and at the same time not + * risking a deadlock (due to a filled SSH2 channel window, caused + * by the stream which you are currently NOT reading from =). + */ + + /* Don't wrap these streams and don't let other threads work on + * these streams while you work with Session.waitForCondition()!!! + */ + + InputStream stdout = sess.getStdout(); + InputStream stderr = sess.getStderr(); + + byte[] buffer = new byte[8192]; + + while (true) + { + if ((stdout.available() == 0) && (stderr.available() == 0)) + { + /* Even though currently there is no data available, it may be that new data arrives + * and the session's underlying channel is closed before we call waitForCondition(). + * This means that EOF and STDOUT_DATA (or STDERR_DATA, or both) may + * be set together. + */ + + int conditions = sess.waitForCondition(ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA + | ChannelCondition.EOF, 2000); + + /* Wait no longer than 2 seconds (= 2000 milliseconds) */ + + if ((conditions & ChannelCondition.TIMEOUT) != 0) + { + /* A timeout occured. */ + throw new IOException("Timeout while waiting for data from peer."); + } + + /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */ + + if ((conditions & ChannelCondition.EOF) != 0) + { + /* The remote side won't send us further data... */ + + if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) + { + /* ... and we have consumed all data in the local arrival window. */ + break; + } + } + + /* OK, either STDOUT_DATA or STDERR_DATA (or both) is set. */ + + // You can be paranoid and check that the library is not going nuts: + // if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) + // throw new IllegalStateException("Unexpected condition result (" + conditions + ")"); + } + + /* If you below replace "while" with "if", then the way the output appears on the local + * stdout and stder streams is more "balanced". Addtionally reducing the buffer size + * will also improve the interleaving, but performance will slightly suffer. + * OKOK, that all matters only if you get HUGE amounts of stdout and stderr data =) + */ + + while (stdout.available() > 0) + { + int len = stdout.read(buffer); + if (len > 0) // this check is somewhat paranoid + System.out.write(buffer, 0, len); + } + + while (stderr.available() > 0) + { + int len = stderr.read(buffer); + if (len > 0) // this check is somewhat paranoid + System.err.write(buffer, 0, len); + } + } + + /* Close this session */ + + sess.close(); + + /* Close the connection */ + + conn.close(); + + } + catch (IOException e) + { + e.printStackTrace(System.err); + System.exit(2); + } + } +} diff --git a/debian/examples/StdoutAndStderr.java b/debian/examples/StdoutAndStderr.java new file mode 100644 index 0000000..257dc6b --- /dev/null +++ b/debian/examples/StdoutAndStderr.java @@ -0,0 +1,93 @@ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import com.trilead.ssh2.Connection; +import com.trilead.ssh2.Session; +import com.trilead.ssh2.StreamGobbler; + +/** + * This example shows how to consume stdout/stderr output + * using two StreamGobblers. This is simpler to program + * than the state machine approach (see SingleThreadStdoutStderr.java), + * but you cannot control the amount of memory that is + * consumed by your application (i.e., in case the other + * side sends you lots of data). + * + * @author Christian Plattner, [email protected] + * @version $Id: StdoutAndStderr.java,v 1.2 2007/10/15 12:49:57 cplattne Exp $ + */ +public class StdoutAndStderr +{ + public static void main(String[] args) + { + String hostname = "127.0.0.1"; + String username = "joe"; + String password = "joespass"; + + try + { + /* Create a connection instance */ + + Connection conn = new Connection(hostname); + + /* Now connect */ + + conn.connect(); + + /* Authenticate */ + + boolean isAuthenticated = conn.authenticateWithPassword(username, password); + + if (isAuthenticated == false) + throw new IOException("Authentication failed."); + + /* Create a session */ + + Session sess = conn.openSession(); + + sess.execCommand("echo \"Text on STDOUT\"; echo \"Text on STDERR\" >&2"); + + InputStream stdout = new StreamGobbler(sess.getStdout()); + InputStream stderr = new StreamGobbler(sess.getStderr()); + + BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(stdout)); + BufferedReader stderrReader = new BufferedReader(new InputStreamReader(stderr)); + + System.out.println("Here is the output from stdout:"); + + while (true) + { + String line = stdoutReader.readLine(); + if (line == null) + break; + System.out.println(line); + } + + System.out.println("Here is the output from stderr:"); + + while (true) + { + String line = stderrReader.readLine(); + if (line == null) + break; + System.out.println(line); + } + + /* Close this session */ + + sess.close(); + + /* Close the connection */ + + conn.close(); + + } + catch (IOException e) + { + e.printStackTrace(System.err); + System.exit(2); + } + } +} diff --git a/debian/examples/SwingShell.java b/debian/examples/SwingShell.java new file mode 100644 index 0000000..1426aca --- /dev/null +++ b/debian/examples/SwingShell.java @@ -0,0 +1,786 @@ +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +import com.trilead.ssh2.Connection; +import com.trilead.ssh2.InteractiveCallback; +import com.trilead.ssh2.KnownHosts; +import com.trilead.ssh2.ServerHostKeyVerifier; +import com.trilead.ssh2.Session; + +/** + * This is a very primitive SSH-2 dumb terminal (Swing based). + * <p> + * The purpose of this class is to demonstrate: + * <ul> + * <li>Verifying server hostkeys with an existing known_hosts file</li> + * <li>Displaying fingerprints of server hostkeys</li> + * <li>Adding a server hostkey to a known_hosts file (+hashing the hostname for security)</li> + * <li>Authentication with DSA, RSA, password and keyboard-interactive methods</li> + * </ul> + * + * @author Christian Plattner, [email protected] + * @version $Id: SwingShell.java,v 1.10 2007/10/15 12:49:57 cplattne Exp $ + * + */ +public class SwingShell +{ + /* + * NOTE: to get this feature to work, replace the "tilde" with your home directory, + * at least my JVM does not understand it. Need to check the specs. + */ + + static final String knownHostPath = "~/.ssh/known_hosts"; + static final String idDSAPath = "~/.ssh/id_dsa"; + static final String idRSAPath = "~/.ssh/id_rsa"; + + JFrame loginFrame = null; + JLabel hostLabel; + JLabel userLabel; + JTextField hostField; + JTextField userField; + JButton loginButton; + + KnownHosts database = new KnownHosts(); + + public SwingShell() + { + File knownHostFile = new File(knownHostPath); + if (knownHostFile.exists()) + { + try + { + database.addHostkeys(knownHostFile); + } + catch (IOException e) + { + } + } + } + + /** + * This dialog displays a number of text lines and a text field. + * The text field can either be plain text or a password field. + */ + class EnterSomethingDialog extends JDialog + { + private static final long serialVersionUID = 1L; + + JTextField answerField; + JPasswordField passwordField; + + final boolean isPassword; + + String answer; + + public EnterSomethingDialog(JFrame parent, String title, String content, boolean isPassword) + { + this(parent, title, new String[] { content }, isPassword); + } + + public EnterSomethingDialog(JFrame parent, String title, String[] content, boolean isPassword) + { + super(parent, title, true); + + this.isPassword = isPassword; + + JPanel pan = new JPanel(); + pan.setLayout(new BoxLayout(pan, BoxLayout.Y_AXIS)); + + for (int i = 0; i < content.length; i++) + { + if ((content[i] == null) || (content[i] == "")) + continue; + JLabel contentLabel = new JLabel(content[i]); + pan.add(contentLabel); + + } + + answerField = new JTextField(20); + passwordField = new JPasswordField(20); + + if (isPassword) + pan.add(passwordField); + else + pan.add(answerField); + + KeyAdapter kl = new KeyAdapter() + { + public void keyTyped(KeyEvent e) + { + if (e.getKeyChar() == '\n') + finish(); + } + }; + + answerField.addKeyListener(kl); + passwordField.addKeyListener(kl); + + getContentPane().add(BorderLayout.CENTER, pan); + + setResizable(false); + pack(); + setLocationRelativeTo(null); + } + + private void finish() + { + if (isPassword) + answer = new String(passwordField.getPassword()); + else + answer = answerField.getText(); + + dispose(); + } + } + + /** + * TerminalDialog is probably the worst terminal emulator ever written - implementing + * a real vt100 is left as an exercise to the reader, i.e., to you =) + * + */ + class TerminalDialog extends JDialog + { + private static final long serialVersionUID = 1L; + + JPanel botPanel; + JButton logoffButton; + JTextArea terminalArea; + + Session sess; + InputStream in; + OutputStream out; + + int x, y; + + /** + * This thread consumes output from the remote server and displays it in + * the terminal window. + * + */ + class RemoteConsumer extends Thread + { + char[][] lines = new char[y][]; + int posy = 0; + int posx = 0; + + private void addText(byte[] data, int len) + { + for (int i = 0; i < len; i++) + { + char c = (char) (data[i] & 0xff); + + if (c == 8) // Backspace, VERASE + { + if (posx < 0) + continue; + posx--; + continue; + } + + if (c == '\r') + { + posx = 0; + continue; + } + + if (c == '\n') + { + posy++; + if (posy >= y) + { + for (int k = 1; k < y; k++) + lines[k - 1] = lines[k]; + posy--; + lines[y - 1] = new char[x]; + for (int k = 0; k < x; k++) + lines[y - 1][k] = ' '; + } + continue; + } + + if (c < 32) + { + continue; + } + + if (posx >= x) + { + posx = 0; + posy++; + if (posy >= y) + { + posy--; + for (int k = 1; k < y; k++) + lines[k - 1] = lines[k]; + lines[y - 1] = new char[x]; + for (int k = 0; k < x; k++) + lines[y - 1][k] = ' '; + } + } + + if (lines[posy] == null) + { + lines[posy] = new char[x]; + for (int k = 0; k < x; k++) + lines[posy][k] = ' '; + } + + lines[posy][posx] = c; + posx++; + } + + StringBuffer sb = new StringBuffer(x * y); + + for (int i = 0; i < lines.length; i++) + { + if (i != 0) + sb.append('\n'); + + if (lines[i] != null) + { + sb.append(lines[i]); + } + + } + setContent(sb.toString()); + } + + public void run() + { + byte[] buff = new byte[8192]; + + try + { + while (true) + { + int len = in.read(buff); + if (len == -1) + return; + addText(buff, len); + } + } + catch (Exception e) + { + } + } + } + + public TerminalDialog(JFrame parent, String title, Session sess, int x, int y) throws IOException + { + super(parent, title, true); + + this.sess = sess; + + in = sess.getStdout(); + out = sess.getStdin(); + + this.x = x; + this.y = y; + + botPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + + logoffButton = new JButton("Logout"); + botPanel.add(logoffButton); + + logoffButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + /* Dispose the dialog, "setVisible(true)" method will return */ + dispose(); + } + }); + + Font f = new Font("Monospaced", Font.PLAIN, 16); + + terminalArea = new JTextArea(y, x); + terminalArea.setFont(f); + terminalArea.setBackground(Color.BLACK); + terminalArea.setForeground(Color.ORANGE); + /* This is a hack. We cannot disable the caret, + * since setting editable to false also changes + * the meaning of the TAB key - and I want to use it in bash. + * Again - this is a simple DEMO terminal =) + */ + terminalArea.setCaretColor(Color.BLACK); + + KeyAdapter kl = new KeyAdapter() + { + public void keyTyped(KeyEvent e) + { + int c = e.getKeyChar(); + + try + { + out.write(c); + } + catch (IOException e1) + { + } + e.consume(); + } + }; + + terminalArea.addKeyListener(kl); + + getContentPane().add(terminalArea, BorderLayout.CENTER); + getContentPane().add(botPanel, BorderLayout.PAGE_END); + + setResizable(false); + pack(); + setLocationRelativeTo(parent); + + new RemoteConsumer().start(); + } + + public void setContent(String lines) + { + // setText is thread safe, it does not have to be called from + // the Swing GUI thread. + terminalArea.setText(lines); + } + } + + /** + * This ServerHostKeyVerifier asks the user on how to proceed if a key cannot be found + * in the in-memory database. + * + */ + class AdvancedVerifier implements ServerHostKeyVerifier + { + public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, + byte[] serverHostKey) throws Exception + { + final String host = hostname; + final String algo = serverHostKeyAlgorithm; + + String message; + + /* Check database */ + + int result = database.verifyHostkey(hostname, serverHostKeyAlgorithm, serverHostKey); + + switch (result) + { + case KnownHosts.HOSTKEY_IS_OK: + return true; + + case KnownHosts.HOSTKEY_IS_NEW: + message = "Do you want to accept the hostkey (type " + algo + ") from " + host + " ?\n"; + break; + + case KnownHosts.HOSTKEY_HAS_CHANGED: + message = "WARNING! Hostkey for " + host + " has changed!\nAccept anyway?\n"; + break; + + default: + throw new IllegalStateException(); + } + + /* Include the fingerprints in the message */ + + String hexFingerprint = KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey); + String bubblebabbleFingerprint = KnownHosts.createBubblebabbleFingerprint(serverHostKeyAlgorithm, + serverHostKey); + + message += "Hex Fingerprint: " + hexFingerprint + "\nBubblebabble Fingerprint: " + bubblebabbleFingerprint; + + /* Now ask the user */ + + int choice = JOptionPane.showConfirmDialog(loginFrame, message); + + if (choice == JOptionPane.YES_OPTION) + { + /* Be really paranoid. We use a hashed hostname entry */ + + String hashedHostname = KnownHosts.createHashedHostname(hostname); + + /* Add the hostkey to the in-memory database */ + + database.addHostkey(new String[] { hashedHostname }, serverHostKeyAlgorithm, serverHostKey); + + /* Also try to add the key to a known_host file */ + + try + { + KnownHosts.addHostkeyToFile(new File(knownHostPath), new String[] { hashedHostname }, + serverHostKeyAlgorithm, serverHostKey); + } + catch (IOException ignore) + { + } + + return true; + } + + if (choice == JOptionPane.CANCEL_OPTION) + { + throw new Exception("The user aborted the server hostkey verification."); + } + + return false; + } + } + + /** + * The logic that one has to implement if "keyboard-interactive" autentication shall be + * supported. + * + */ + class InteractiveLogic implements InteractiveCallback + { + int promptCount = 0; + String lastError; + + public InteractiveLogic(String lastError) + { + this.lastError = lastError; + } + + /* the callback may be invoked several times, depending on how many questions-sets the server sends */ + + public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, + boolean[] echo) throws IOException + { + String[] result = new String[numPrompts]; + + for (int i = 0; i < numPrompts; i++) + { + /* Often, servers just send empty strings for "name" and "instruction" */ + + String[] content = new String[] { lastError, name, instruction, prompt[i] }; + + if (lastError != null) + { + /* show lastError only once */ + lastError = null; + } + + EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, "Keyboard Interactive Authentication", + content, !echo[i]); + + esd.setVisible(true); + + if (esd.answer == null) + throw new IOException("Login aborted by user"); + + result[i] = esd.answer; + promptCount++; + } + + return result; + } + + /* We maintain a prompt counter - this enables the detection of situations where the ssh + * server is signaling "authentication failed" even though it did not send a single prompt. + */ + + public int getPromptCount() + { + return promptCount; + } + } + + /** + * The SSH-2 connection is established in this thread. + * If we would not use a separate thread (e.g., put this code in + * the event handler of the "Login" button) then the GUI would not + * be responsive (missing window repaints if you move the window etc.) + */ + class ConnectionThread extends Thread + { + String hostname; + String username; + + public ConnectionThread(String hostname, String username) + { + this.hostname = hostname; + this.username = username; + } + + public void run() + { + Connection conn = new Connection(hostname); + + try + { + /* + * + * CONNECT AND VERIFY SERVER HOST KEY (with callback) + * + */ + + String[] hostkeyAlgos = database.getPreferredServerHostkeyAlgorithmOrder(hostname); + + if (hostkeyAlgos != null) + conn.setServerHostKeyAlgorithms(hostkeyAlgos); + + conn.connect(new AdvancedVerifier()); + + /* + * + * AUTHENTICATION PHASE + * + */ + + boolean enableKeyboardInteractive = true; + boolean enableDSA = true; + boolean enableRSA = true; + + String lastError = null; + + while (true) + { + if ((enableDSA || enableRSA) && conn.isAuthMethodAvailable(username, "publickey")) + { + if (enableDSA) + { + File key = new File(idDSAPath); + + if (key.exists()) + { + EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, "DSA Authentication", + new String[] { lastError, "Enter DSA private key password:" }, true); + esd.setVisible(true); + + boolean res = conn.authenticateWithPublicKey(username, key, esd.answer); + + if (res == true) + break; + + lastError = "DSA authentication failed."; + } + enableDSA = false; // do not try again + } + + if (enableRSA) + { + File key = new File(idRSAPath); + + if (key.exists()) + { + EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, "RSA Authentication", + new String[] { lastError, "Enter RSA private key password:" }, true); + esd.setVisible(true); + + boolean res = conn.authenticateWithPublicKey(username, key, esd.answer); + + if (res == true) + break; + + lastError = "RSA authentication failed."; + } + enableRSA = false; // do not try again + } + + continue; + } + + if (enableKeyboardInteractive && conn.isAuthMethodAvailable(username, "keyboard-interactive")) + { + InteractiveLogic il = new InteractiveLogic(lastError); + + boolean res = conn.authenticateWithKeyboardInteractive(username, il); + + if (res == true) + break; + + if (il.getPromptCount() == 0) + { + // aha. the server announced that it supports "keyboard-interactive", but when + // we asked for it, it just denied the request without sending us any prompt. + // That happens with some server versions/configurations. + // We just disable the "keyboard-interactive" method and notify the user. + + lastError = "Keyboard-interactive does not work."; + + enableKeyboardInteractive = false; // do not try this again + } + else + { + lastError = "Keyboard-interactive auth failed."; // try again, if possible + } + + continue; + } + + if (conn.isAuthMethodAvailable(username, "password")) + { + final EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, + "Password Authentication", + new String[] { lastError, "Enter password for " + username }, true); + + esd.setVisible(true); + + if (esd.answer == null) + throw new IOException("Login aborted by user"); + + boolean res = conn.authenticateWithPassword(username, esd.answer); + + if (res == true) + break; + + lastError = "Password authentication failed."; // try again, if possible + + continue; + } + + throw new IOException("No supported authentication methods available."); + } + + /* + * + * AUTHENTICATION OK. DO SOMETHING. + * + */ + + Session sess = conn.openSession(); + + int x_width = 90; + int y_width = 30; + + sess.requestPTY("dumb", x_width, y_width, 0, 0, null); + sess.startShell(); + + TerminalDialog td = new TerminalDialog(loginFrame, username + "@" + hostname, sess, x_width, y_width); + + /* The following call blocks until the dialog has been closed */ + + td.setVisible(true); + + } + catch (IOException e) + { + //e.printStackTrace(); + JOptionPane.showMessageDialog(loginFrame, "Exception: " + e.getMessage()); + } + + /* + * + * CLOSE THE CONNECTION. + * + */ + + conn.close(); + + /* + * + * CLOSE THE LOGIN FRAME - APPLICATION WILL BE EXITED (no more frames) + * + */ + + Runnable r = new Runnable() + { + public void run() + { + loginFrame.dispose(); + } + }; + + SwingUtilities.invokeLater(r); + } + } + + void loginPressed() + { + String hostname = hostField.getText().trim(); + String username = userField.getText().trim(); + + if ((hostname.length() == 0) || (username.length() == 0)) + { + JOptionPane.showMessageDialog(loginFrame, "Please fill out both fields!"); + return; + } + + loginButton.setEnabled(false); + hostField.setEnabled(false); + userField.setEnabled(false); + + ConnectionThread ct = new ConnectionThread(hostname, username); + + ct.start(); + } + + void showGUI() + { + loginFrame = new JFrame("Trilead SSH-2 for Java SwingShell"); + + hostLabel = new JLabel("Hostname:"); + userLabel = new JLabel("Username:"); + + hostField = new JTextField("", 20); + userField = new JTextField("", 10); + + loginButton = new JButton("Login"); + + loginButton.addActionListener(new ActionListener() + { + public void actionPerformed(java.awt.event.ActionEvent e) + { + loginPressed(); + } + }); + + JPanel loginPanel = new JPanel(); + + loginPanel.add(hostLabel); + loginPanel.add(hostField); + loginPanel.add(userLabel); + loginPanel.add(userField); + loginPanel.add(loginButton); + + loginFrame.getRootPane().setDefaultButton(loginButton); + + loginFrame.getContentPane().add(loginPanel, BorderLayout.PAGE_START); + //loginFrame.getContentPane().add(textArea, BorderLayout.CENTER); + + loginFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + loginFrame.pack(); + loginFrame.setResizable(false); + loginFrame.setLocationRelativeTo(null); + loginFrame.setVisible(true); + } + + void startGUI() + { + Runnable r = new Runnable() + { + public void run() + { + showGUI(); + } + }; + + SwingUtilities.invokeLater(r); + + } + + public static void main(String[] args) + { + SwingShell client = new SwingShell(); + client.startGUI(); + } +} diff --git a/debian/examples/UsingKnownHosts.java b/debian/examples/UsingKnownHosts.java new file mode 100644 index 0000000..708e290 --- /dev/null +++ b/debian/examples/UsingKnownHosts.java @@ -0,0 +1,86 @@ +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import com.trilead.ssh2.Connection; +import com.trilead.ssh2.KnownHosts; +import com.trilead.ssh2.Session; +import com.trilead.ssh2.StreamGobbler; + +/** + * This example shows how to deal with "known_hosts" files. + * + * @author Christian Plattner, [email protected] + * @version $Id: UsingKnownHosts.java,v 1.2 2007/10/15 12:49:57 cplattne Exp $ + */ +public class UsingKnownHosts +{ + static KnownHosts database = new KnownHosts(); + + public static void main(String[] args) throws IOException + { + String hostname = "somehost"; + String username = "joe"; + String password = "joespass"; + + File knownHosts = new File("~/.ssh/known_hosts"); + + try + { + /* Load known_hosts file into in-memory database */ + + if (knownHosts.exists()) + database.addHostkeys(knownHosts); + + /* Create a connection instance */ + + Connection conn = new Connection(hostname); + + /* Now connect and use the SimpleVerifier */ + + conn.connect(new SimpleVerifier(database)); + + /* Authenticate */ + + boolean isAuthenticated = conn.authenticateWithPassword(username, password); + + if (isAuthenticated == false) + throw new IOException("Authentication failed."); + + /* Create a session */ + + Session sess = conn.openSession(); + + sess.execCommand("uname -a && date && uptime && who"); + + InputStream stdout = new StreamGobbler(sess.getStdout()); + BufferedReader br = new BufferedReader(new InputStreamReader(stdout)); + + System.out.println("Here is some information about the remote host:"); + + while (true) + { + String line = br.readLine(); + if (line == null) + break; + System.out.println(line); + } + + /* Close this session */ + + sess.close(); + + /* Close the connection */ + + conn.close(); + + } + catch (IOException e) + { + e.printStackTrace(System.err); + System.exit(2); + } + } +} diff --git a/debian/libtrilead-ssh2-java.docs b/debian/libtrilead-ssh2-java.docs index 2754efd..efc2692 100644 --- a/debian/libtrilead-ssh2-java.docs +++ b/debian/libtrilead-ssh2-java.docs @@ -1,3 +1,3 @@ -README.txt -examples -faq/FAQ.html +debian/README.txt +debian/examples +debian/FAQ.html diff --git a/debian/rules b/debian/rules index a0bc1bb..cf662bb 100755 --- a/debian/rules +++ b/debian/rules @@ -8,6 +8,3 @@ export JAVA_HOME=/usr/lib/jvm/default-java %: dh --with javahelper $@ -override_dh_installchangelogs: - dh_installchangelogs HISTORY.txt - -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/trilead-ssh2.git _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

