mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-07 15:50:57 +03:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2abc4b397 | ||
|
|
fe0d42fc97 | ||
|
|
19e4670c24 | ||
|
|
fbd6e00720 | ||
|
|
f69cdb1505 | ||
|
|
135b1c819b | ||
|
|
9c51b862cd | ||
|
|
a6353cbb2d | ||
|
|
f11055a726 | ||
|
|
da98153ab6 | ||
|
|
70d4dbd88a | ||
|
|
b2a74935d4 | ||
|
|
f3c072fb06 | ||
|
|
738b317dcf | ||
|
|
d87e0c2da2 | ||
|
|
72ff74c99d | ||
|
|
5cb4f1be43 | ||
|
|
693379feb4 | ||
|
|
98529f733c | ||
|
|
582f789db4 | ||
|
|
c16ccc2644 | ||
|
|
5c3ba2282c | ||
|
|
e765536fa1 | ||
|
|
068fbdfc55 | ||
|
|
f4f9249b35 | ||
|
|
252fcebeac | ||
|
|
a7ccccaacf | ||
|
|
2420027a58 | ||
|
|
b54a4abfce | ||
|
|
0c28deab88 | ||
|
|
12c79dc2b2 | ||
|
|
6ef3dacc81 | ||
|
|
700998d064 | ||
|
|
dea6e3db91 | ||
|
|
dc5ff451f0 | ||
|
|
ca521500ed | ||
|
|
ca07e0d25a | ||
|
|
f372ae782a | ||
|
|
460a730879 | ||
|
|
25a18c6fd7 | ||
|
|
53389e6989 | ||
|
|
2f1b2c4e77 | ||
|
|
09b93ec11a | ||
|
|
63b321e8d1 | ||
|
|
49e3d4ec27 | ||
|
|
8e9a773e92 | ||
|
|
f30afe50f4 | ||
|
|
4af8fab434 | ||
|
|
436e5774a8 | ||
|
|
7a2850d760 | ||
|
|
53d8d548cf | ||
|
|
89d03361ba | ||
|
|
db4185608d | ||
|
|
71b6cee424 | ||
|
|
bdb1fe73e3 | ||
|
|
e327e2e8ad | ||
|
|
66fd4a4823 | ||
|
|
8be0e1c8b0 | ||
|
|
37de8faf9b | ||
|
|
9e92463ff2 | ||
|
|
9cbb75084a | ||
|
|
54ee1a6917 | ||
|
|
dd1be04a61 | ||
|
|
839021cd2b | ||
|
|
e6f2ed3035 | ||
|
|
6581772c18 |
57
NOTICE
57
NOTICE
@@ -6,3 +6,60 @@ The Apache Software Foundation (http://www.apache.org/):
|
||||
|
||||
- Apache MINA SSHD
|
||||
- Apache Commons-Net
|
||||
|
||||
|
||||
// Apache Mina SSHD notice
|
||||
|
||||
=========================================================================
|
||||
== NOTICE file for use with the Apache License, Version 2.0, ==
|
||||
== in this case for the SSHD distribution. ==
|
||||
=========================================================================
|
||||
|
||||
This product contains software developped by JCraft,Inc. and subject to
|
||||
the following license:
|
||||
|
||||
Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, 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.
|
||||
--------------------------------------------------------------------------------
|
||||
Copyright (c) 2000 - 2006 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@@ -37,7 +37,7 @@ If you need something that is not implemented, it shouldn't be too hard to add (
|
||||
Dependencies
|
||||
-------------
|
||||
|
||||
slf4j_ is required. bouncycastle_ is highly recommended and required for using some of the crypto algorithms.
|
||||
Java 6+. slf4j_ is required. bouncycastle_ is highly recommended and required for using some of the crypto algorithms.
|
||||
jzlib_ is required for using zlib compression.
|
||||
|
||||
|
||||
|
||||
116
pom.xml
116
pom.xml
@@ -6,7 +6,7 @@
|
||||
<groupId>net.schmizz</groupId>
|
||||
<artifactId>sshj</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.1.1</version>
|
||||
<version>0.2.3</version>
|
||||
|
||||
<name>sshj</name>
|
||||
<description>SSHv2 library for Java</description>
|
||||
@@ -41,61 +41,39 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15</artifactId>
|
||||
<artifactId>bcprov-jdk16</artifactId>
|
||||
<version>1.45</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jzlib</artifactId>
|
||||
<version>1.0.7</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-core</artifactId>
|
||||
<version>0.3.0</version>
|
||||
<version>0.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>0.9.20</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.15</version>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>0.9.20</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jdmk</groupId>
|
||||
<artifactId>jmxtools</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jmx</groupId>
|
||||
<artifactId>jmxri</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.jms</groupId>
|
||||
<artifactId>jms</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>mail</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>oro</groupId>
|
||||
<artifactId>oro</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -109,6 +87,10 @@
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@@ -118,8 +100,8 @@
|
||||
<excludes>
|
||||
<exclude>examples/*.java</exclude>
|
||||
</excludes>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@@ -127,6 +109,24 @@
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.2-beta-5</version>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/assemble/examples.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
@@ -172,11 +172,11 @@
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>dev</id>
|
||||
<id>full-deps</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15</artifactId>
|
||||
<artifactId>bcprov-jdk16</artifactId>
|
||||
<version>1.45</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -185,36 +185,14 @@
|
||||
<version>1.0.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>0.9.20</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.15</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jdmk</groupId>
|
||||
<artifactId>jmxtools</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jmx</groupId>
|
||||
<artifactId>jmxri</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.jms</groupId>
|
||||
<artifactId>jms</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>mail</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>oro</groupId>
|
||||
<artifactId>oro</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>0.9.20</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
15
src/assemble/examples.xml
Normal file
15
src/assemble/examples.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||
<id>examples</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>src/main/java/examples</directory>
|
||||
<includes/>
|
||||
<outputDirectory>examples</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
@@ -23,10 +23,6 @@ import java.io.IOException;
|
||||
/** This examples demonstrates how a remote command can be executed. */
|
||||
public class Exec {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
@@ -37,9 +33,8 @@ public class Exec {
|
||||
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
|
||||
Command cmd = ssh.startSession().exec("ping google.com -n 1");
|
||||
Command cmd = ssh.startSession().exec("ping -c 1 google.com");
|
||||
|
||||
// Pipe.pipe(cmd.getInputStream(), System.out, cmd.getLocalMaxPacketSize(), false);
|
||||
System.out.print(cmd.getOutputAsString());
|
||||
System.out.println("\n** exit status: " + cmd.getExitStatus());
|
||||
|
||||
|
||||
@@ -26,11 +26,6 @@ import java.net.InetSocketAddress;
|
||||
*/
|
||||
public class LocalPF {
|
||||
|
||||
// static
|
||||
// {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
@@ -46,7 +41,6 @@ public class LocalPF {
|
||||
* _We_ listen on localhost:8080 and forward all connections on to server, which then forwards it to
|
||||
* google.com:80
|
||||
*/
|
||||
|
||||
ssh.newLocalPortForwarder(new InetSocketAddress("localhost", 8080), "google.com", 80)
|
||||
.listen();
|
||||
|
||||
|
||||
@@ -28,10 +28,6 @@ import java.net.InetSocketAddress;
|
||||
*/
|
||||
public class RemotePF {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException {
|
||||
SSHClient client = new SSHClient();
|
||||
|
||||
@@ -28,10 +28,6 @@ import java.io.IOException;
|
||||
/** A very rudimentary psuedo-terminal based on console I/O. */
|
||||
class RudimentaryPTY {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException {
|
||||
|
||||
@@ -70,7 +66,6 @@ class RudimentaryPTY {
|
||||
|
||||
if (shell != null)
|
||||
shell.close();
|
||||
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,20 +22,17 @@ import java.io.IOException;
|
||||
/** This example demonstrates downloading of a file over SCP from the SSH server. */
|
||||
public class SCPDownload {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
// ssh.useCompression(); // Can lead to significant speedup
|
||||
// ssh.useCompression(); // Can lead to significant speedup (needs JZlib in classpath)
|
||||
ssh.loadKnownHosts();
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
ssh.newSCPFileTransfer()
|
||||
.download("well", "/tmp/");
|
||||
final String src = "test_file";
|
||||
final String target = "/tmp/";
|
||||
ssh.newSCPFileTransfer().download(src, target);
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
|
||||
@@ -17,15 +17,12 @@ package examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/** This example demonstrates uploading of a file over SCP to the SSH server. */
|
||||
public class SCPUpload {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException, ClassNotFoundException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
@@ -35,10 +32,12 @@ public class SCPUpload {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
|
||||
// Present here to demo algorithm renegotiation - could have just put this before connect()
|
||||
// Make sure JZlib is in classpath for this to work
|
||||
ssh.useCompression();
|
||||
|
||||
ssh.newSCPFileTransfer()
|
||||
.upload("/Users/shikhar/well", "/tmp/");
|
||||
final String src = System.getProperty("user.home") + File.separator + "test_file";
|
||||
final String target = "/tmp/";
|
||||
ssh.newSCPFileTransfer().upload(src, target);
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
|
||||
@@ -22,10 +22,6 @@ import java.io.IOException;
|
||||
/** This example demonstrates downloading of a file over SFTP from the SSH server. */
|
||||
public class SFTPDownload {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
@@ -33,7 +29,9 @@ public class SFTPDownload {
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
ssh.newSFTPClient().get("well", "/tmp/");
|
||||
final String src = "test_file";
|
||||
final String target = "/tmp/";
|
||||
ssh.newSFTPClient().get(src, target);
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
|
||||
@@ -17,16 +17,12 @@ package examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/** This example demonstrates uploading of a file over SFTP to the SSH server. */
|
||||
public class SFTPUpload {
|
||||
|
||||
// static
|
||||
// {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
@@ -34,7 +30,9 @@ public class SFTPUpload {
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
ssh.newSFTPClient().put("/Users/shikhar/well", "/tmp/");
|
||||
final String src = System.getProperty("user.home") + File.separator + "test_file";
|
||||
final String target = "/tmp/";
|
||||
ssh.newSFTPClient().put(src, target);
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
|
||||
@@ -27,10 +27,6 @@ import java.net.InetSocketAddress;
|
||||
/** This example demonstrates how forwarding X11 connections from a remote host can be accomplished. */
|
||||
public class X11 {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException, InterruptedException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
@@ -59,7 +55,7 @@ public class X11 {
|
||||
*/
|
||||
sess.reqX11Forwarding("MIT-MAGIC-COOKIE-1", "b0956167c9ad8f34c8a2788878307dc9", 0);
|
||||
|
||||
Command cmd = sess.exec("mate");
|
||||
Command cmd = sess.exec("/usr/X11/bin/xcalc");
|
||||
|
||||
new StreamCopier("stdout", cmd.getInputStream(), System.out).start();
|
||||
new StreamCopier("stderr", cmd.getErrorStream(), System.err).start();
|
||||
|
||||
@@ -44,29 +44,35 @@ public abstract class AbstractService
|
||||
timeout = trans.getTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Message msg, SSHPacket buf)
|
||||
throws SSHException {
|
||||
trans.sendUnimplemented();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyError(SSHException error) {
|
||||
log.debug("Was notified of {}", error.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyUnimplemented(long seqNum)
|
||||
throws SSHException {
|
||||
throw new SSHException(DisconnectReason.PROTOCOL_ERROR, "Unexpected: SSH_MSG_UNIMPLEMENTED");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDisconnect()
|
||||
throws SSHException {
|
||||
log.debug("Was notified of disconnect");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void request()
|
||||
throws TransportException {
|
||||
final Service active = trans.getService();
|
||||
|
||||
@@ -142,5 +142,5 @@ public interface Config {
|
||||
* @param version software version info
|
||||
*/
|
||||
void setVersion(String version);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -62,34 +62,42 @@ public class ConfigImpl
|
||||
private List<Factory.Named<Signature>> signatureFactories;
|
||||
private List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories;
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<Cipher>> getCipherFactories() {
|
||||
return cipherFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<Compression>> getCompressionFactories() {
|
||||
return compressionFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<FileKeyProvider>> getFileKeyProviderFactories() {
|
||||
return fileKeyProviderFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<KeyExchange>> getKeyExchangeFactories() {
|
||||
return kexFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<MAC>> getMACFactories() {
|
||||
return macFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Factory<Random> getRandomFactory() {
|
||||
return randomFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<Signature>> getSignatureFactories() {
|
||||
return signatureFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
@@ -98,6 +106,7 @@ public class ConfigImpl
|
||||
setCipherFactories(Arrays.asList(cipherFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCipherFactories(List<Factory.Named<Cipher>> cipherFactories) {
|
||||
this.cipherFactories = cipherFactories;
|
||||
}
|
||||
@@ -106,6 +115,7 @@ public class ConfigImpl
|
||||
setCompressionFactories(Arrays.asList(compressionFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCompressionFactories(List<Factory.Named<Compression>> compressionFactories) {
|
||||
this.compressionFactories = compressionFactories;
|
||||
}
|
||||
@@ -114,6 +124,7 @@ public class ConfigImpl
|
||||
setFileKeyProviderFactories(Arrays.asList(fileKeyProviderFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFileKeyProviderFactories(List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories) {
|
||||
this.fileKeyProviderFactories = fileKeyProviderFactories;
|
||||
}
|
||||
@@ -122,6 +133,7 @@ public class ConfigImpl
|
||||
setKeyExchangeFactories(Arrays.asList(kexFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeyExchangeFactories(List<Factory.Named<KeyExchange>> kexFactories) {
|
||||
this.kexFactories = kexFactories;
|
||||
}
|
||||
@@ -130,10 +142,12 @@ public class ConfigImpl
|
||||
setMACFactories(Arrays.asList(macFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMACFactories(List<Factory.Named<MAC>> macFactories) {
|
||||
this.macFactories = macFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRandomFactory(Factory<Random> randomFactory) {
|
||||
this.randomFactory = randomFactory;
|
||||
}
|
||||
@@ -142,10 +156,12 @@ public class ConfigImpl
|
||||
setSignatureFactories(Arrays.asList(signatureFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSignatureFactories(List<Factory.Named<Signature>> signatureFactories) {
|
||||
this.signatureFactories = signatureFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@@ -49,11 +49,14 @@ import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
|
||||
import net.schmizz.sshj.userauth.keyprovider.KeyPairWrapper;
|
||||
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
|
||||
import net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil;
|
||||
import net.schmizz.sshj.userauth.method.AuthKeyboardInteractive;
|
||||
import net.schmizz.sshj.userauth.method.AuthMethod;
|
||||
import net.schmizz.sshj.userauth.method.AuthPassword;
|
||||
import net.schmizz.sshj.userauth.method.AuthPublickey;
|
||||
import net.schmizz.sshj.userauth.method.PasswordResponseProvider;
|
||||
import net.schmizz.sshj.userauth.password.PasswordFinder;
|
||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||
import net.schmizz.sshj.userauth.password.Resource;
|
||||
import net.schmizz.sshj.xfer.scp.SCPFileTransfer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -87,7 +90,6 @@ import java.util.List;
|
||||
* <p/>
|
||||
* <em>A simple example:</em>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <pre>
|
||||
* client = new SSHClient();
|
||||
* client.initUserKnownHosts();
|
||||
@@ -165,6 +167,7 @@ public class SSHClient
|
||||
*/
|
||||
public void addHostKeyVerifier(final String host, final int port, final String fingerprint) {
|
||||
addHostKeyVerifier(new HostKeyVerifier() {
|
||||
@Override
|
||||
public boolean verify(String h, int p, PublicKey k) {
|
||||
return host.equals(h) && port == p && SecurityUtils.getFingerprint(k).equals(fingerprint);
|
||||
}
|
||||
@@ -202,36 +205,8 @@ public class SSHClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method. The {@code password} array is
|
||||
* blanked out after use.
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param password the password to use for authentication
|
||||
*
|
||||
* @throws UserAuthException in case of authentication failure
|
||||
* @throws TransportException if there was a transport-layer error
|
||||
*/
|
||||
public void authPassword(String username, char[] password)
|
||||
throws UserAuthException, TransportException {
|
||||
authPassword(username, PasswordUtils.createOneOff(password));
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method.
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param pfinder the {@link PasswordFinder} to use for authentication
|
||||
*
|
||||
* @throws UserAuthException in case of authentication failure
|
||||
* @throws TransportException if there was a transport-layer error
|
||||
*/
|
||||
public void authPassword(String username, PasswordFinder pfinder)
|
||||
throws UserAuthException, TransportException {
|
||||
auth(username, new AuthPassword(pfinder));
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method.
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method and as a fallback basic
|
||||
* challenge-response authentication.
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param password the password to use for authentication
|
||||
@@ -244,6 +219,52 @@ public class SSHClient
|
||||
authPassword(username, password.toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method and as a fallback basic
|
||||
* challenge-response authentication.. The {@code password} array is blanked out after use.
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param password the password to use for authentication
|
||||
*
|
||||
* @throws UserAuthException in case of authentication failure
|
||||
* @throws TransportException if there was a transport-layer error
|
||||
*/
|
||||
public void authPassword(final String username, final char[] password)
|
||||
throws UserAuthException, TransportException {
|
||||
try {
|
||||
authPassword(username, new PasswordFinder() {
|
||||
|
||||
@Override
|
||||
public char[] reqPassword(Resource<?> resource) {
|
||||
return password.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRetry(Resource<?> resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
} finally {
|
||||
PasswordUtils.blankOut(password);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method and as a fallback basic
|
||||
* challenge-response authentication.
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param pfinder the {@link PasswordFinder} to use for authentication
|
||||
*
|
||||
* @throws UserAuthException in case of authentication failure
|
||||
* @throws TransportException if there was a transport-layer error
|
||||
*/
|
||||
public void authPassword(String username, PasswordFinder pfinder)
|
||||
throws UserAuthException, TransportException {
|
||||
auth(username, new AuthPassword(pfinder), new AuthKeyboardInteractive(new PasswordResponseProvider(pfinder)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "publickey"} authentication method, with keys from some common
|
||||
* locations on the file system. This method relies on {@code ~/.ssh/id_rsa} and {@code ~/.ssh/id_dsa}.
|
||||
@@ -587,6 +608,7 @@ public class SSHClient
|
||||
doKex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session startSession()
|
||||
throws ConnectionException, TransportException {
|
||||
assert isConnected() && isAuthenticated();
|
||||
@@ -607,7 +629,7 @@ public class SSHClient
|
||||
* @throws TransportException if an error occurs during renegotiation
|
||||
*/
|
||||
public void useCompression()
|
||||
throws ClassNotFoundException, TransportException {
|
||||
throws TransportException {
|
||||
trans.getConfig().setCompressionFactories(Arrays.asList(
|
||||
new DelayedZlibCompression.Factory(),
|
||||
new ZlibCompression.Factory(),
|
||||
|
||||
@@ -120,8 +120,6 @@ abstract class SocketClient {
|
||||
output.close();
|
||||
output = null;
|
||||
}
|
||||
input = null;
|
||||
output = null;
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
|
||||
@@ -38,12 +38,7 @@ package net.schmizz.sshj.common;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.DSAPublicKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.DSAPublicKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Buffer<T extends Buffer<T>> {
|
||||
@@ -445,71 +440,34 @@ public class Buffer<T extends Buffer<T>> {
|
||||
* This is useful when a plaintext password needs to be sent. If {@code passwd} is {@code null}, an empty string is
|
||||
* written.
|
||||
*
|
||||
* @param passwd (null-ok) the password as a character array
|
||||
* @param str (null-ok) the password as a character array
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T putPassword(char[] passwd) {
|
||||
if (passwd == null)
|
||||
public T putSensitiveString(char[] str) {
|
||||
if (str == null)
|
||||
return putString("");
|
||||
putInt(passwd.length);
|
||||
ensureCapacity(passwd.length);
|
||||
for (char c : passwd)
|
||||
putInt(str.length);
|
||||
ensureCapacity(str.length);
|
||||
for (char c : str)
|
||||
data[wpos++] = (byte) c;
|
||||
Arrays.fill(passwd, ' ');
|
||||
Arrays.fill(str, ' ');
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public PublicKey readPublicKey() {
|
||||
try {
|
||||
switch (KeyType.fromString(readString())) {
|
||||
case RSA: {
|
||||
BigInteger e = readMPInt();
|
||||
BigInteger n = readMPInt();
|
||||
KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
|
||||
return keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
||||
}
|
||||
case DSA: {
|
||||
BigInteger p = readMPInt();
|
||||
BigInteger q = readMPInt();
|
||||
BigInteger g = readMPInt();
|
||||
BigInteger y = readMPInt();
|
||||
KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
|
||||
return keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
|
||||
}
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
final String type = readString();
|
||||
return KeyType.fromString(type).readPubKeyFromBuffer(type, this);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T putPublicKey(PublicKey key) {
|
||||
final KeyType type = KeyType.fromKey(key);
|
||||
switch (type) {
|
||||
case RSA: {
|
||||
final RSAPublicKey rsaKey = (RSAPublicKey) key;
|
||||
putString(type.toString()) // ssh-rsa
|
||||
.putMPInt(rsaKey.getPublicExponent()) // e
|
||||
.putMPInt(rsaKey.getModulus()); // n
|
||||
break;
|
||||
}
|
||||
case DSA: {
|
||||
final DSAPublicKey dsaKey = (DSAPublicKey) key;
|
||||
putString(type.toString()) // ssh-dss
|
||||
.putMPInt(dsaKey.getParams().getP()) // p
|
||||
.putMPInt(dsaKey.getParams().getQ()) // q
|
||||
.putMPInt(dsaKey.getParams().getG()) // g
|
||||
.putMPInt(dsaKey.getY()); // y
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new SSHRuntimeException("Don't know how to encode key: " + key);
|
||||
}
|
||||
KeyType.fromKey(key).putPubKeyIntoBuffer(key, this);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,47 +15,117 @@
|
||||
*/
|
||||
package net.schmizz.sshj.common;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.DSAPrivateKey;
|
||||
import java.security.interfaces.DSAPublicKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.DSAPublicKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
|
||||
/** Type of key e.g. rsa, dsa */
|
||||
public enum KeyType {
|
||||
|
||||
/** SSH identifier for RSA keys */
|
||||
RSA("ssh-rsa", new KeyChecker() {
|
||||
public boolean isMyType(Key key) {
|
||||
RSA("ssh-rsa") {
|
||||
|
||||
@Override
|
||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
||||
throws GeneralSecurityException {
|
||||
final BigInteger e = buf.readMPInt();
|
||||
final BigInteger n = buf.readMPInt();
|
||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
|
||||
return keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
||||
|
||||
}
|
||||
@Override
|
||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
final RSAPublicKey rsaKey = (RSAPublicKey) pk;
|
||||
buf.putString(sType)
|
||||
.putMPInt(rsaKey.getPublicExponent()) // e
|
||||
.putMPInt(rsaKey.getModulus()); // n
|
||||
|
||||
}
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return (key instanceof RSAPublicKey || key instanceof RSAPrivateKey);
|
||||
}
|
||||
}),
|
||||
|
||||
},
|
||||
|
||||
/** SSH identifier for DSA keys */
|
||||
DSA("ssh-dss", new KeyChecker() {
|
||||
public boolean isMyType(Key key) {
|
||||
DSA("ssh-dss") {
|
||||
|
||||
@Override
|
||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
||||
throws GeneralSecurityException {
|
||||
final BigInteger p = buf.readMPInt();
|
||||
final BigInteger q = buf.readMPInt();
|
||||
final BigInteger g = buf.readMPInt();
|
||||
final BigInteger y = buf.readMPInt();
|
||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
|
||||
return keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
|
||||
}
|
||||
@Override
|
||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
final DSAPublicKey dsaKey = (DSAPublicKey) pk;
|
||||
buf.putString(sType)
|
||||
.putMPInt(dsaKey.getParams().getP()) // p
|
||||
.putMPInt(dsaKey.getParams().getQ()) // q
|
||||
.putMPInt(dsaKey.getParams().getG()) // g
|
||||
.putMPInt(dsaKey.getY()); // y
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return (key instanceof DSAPublicKey || key instanceof DSAPrivateKey);
|
||||
}
|
||||
}),
|
||||
|
||||
},
|
||||
|
||||
/** Unrecognized */
|
||||
UNKNOWN("unknown", null);
|
||||
UNKNOWN("unknown") {
|
||||
|
||||
private static interface KeyChecker {
|
||||
boolean isMyType(Key key);
|
||||
}
|
||||
@Override
|
||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
||||
throws GeneralSecurityException {
|
||||
throw new UnsupportedOperationException("Don't know how to decode key:" + type);
|
||||
}
|
||||
|
||||
private final String sType;
|
||||
private final KeyChecker checker;
|
||||
@Override
|
||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
throw new UnsupportedOperationException("Don't know how to encode key: " + pk);
|
||||
}
|
||||
|
||||
private KeyType(String type, KeyChecker checker) {
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
protected final String sType;
|
||||
|
||||
private KeyType(String type) {
|
||||
this.sType = type;
|
||||
this.checker = checker;
|
||||
}
|
||||
|
||||
public abstract PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
||||
throws GeneralSecurityException;
|
||||
|
||||
public abstract void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
|
||||
|
||||
|
||||
protected abstract boolean isMyType(Key key);
|
||||
|
||||
public static KeyType fromKey(Key key) {
|
||||
for (KeyType kt : values())
|
||||
if (kt.checker != null && kt.checker.isMyType((key)))
|
||||
if (kt.isMyType((key)))
|
||||
return kt;
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ public class SSHException
|
||||
|
||||
public static final ExceptionChainer<SSHException> chainer = new ExceptionChainer<SSHException>() {
|
||||
|
||||
@Override
|
||||
public SSHException chain(Throwable t) {
|
||||
if (t instanceof SSHException)
|
||||
return (SSHException) t;
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class StreamCopier
|
||||
extends Thread {
|
||||
@@ -34,18 +33,23 @@ public class StreamCopier
|
||||
}
|
||||
|
||||
public static ErrorCallback closeOnErrorCallback(final Closeable... toClose) {
|
||||
final Closeable[] closeables = Arrays.copyOf(toClose, toClose.length);
|
||||
return new ErrorCallback() {
|
||||
@Override
|
||||
public void onError(IOException ioe) {
|
||||
IOUtils.closeQuietly(closeables);
|
||||
IOUtils.closeQuietly(toClose);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static long copy(InputStream in, OutputStream out, int bufSize, boolean keepFlushing)
|
||||
public interface Listener {
|
||||
void reportProgress(long transferred);
|
||||
}
|
||||
|
||||
public static long copy(InputStream in, OutputStream out, int bufSize, boolean keepFlushing, Listener listener)
|
||||
throws IOException {
|
||||
long count = 0;
|
||||
|
||||
final boolean reportProgress = listener != null;
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
final byte[] buf = new byte[bufSize];
|
||||
@@ -55,6 +59,8 @@ public class StreamCopier
|
||||
count += read;
|
||||
if (keepFlushing)
|
||||
out.flush();
|
||||
if (reportProgress)
|
||||
listener.reportProgress(count);
|
||||
}
|
||||
if (!keepFlushing)
|
||||
out.flush();
|
||||
@@ -66,6 +72,11 @@ public class StreamCopier
|
||||
return count;
|
||||
}
|
||||
|
||||
public static long copy(InputStream in, OutputStream out, int bufSize, boolean keepFlushing)
|
||||
throws IOException {
|
||||
return copy(in, out, bufSize, keepFlushing, null);
|
||||
}
|
||||
|
||||
public static String copyStreamToString(InputStream stream)
|
||||
throws IOException {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
@@ -84,6 +95,7 @@ public class StreamCopier
|
||||
private boolean keepFlushing = true;
|
||||
|
||||
private ErrorCallback errCB = new ErrorCallback() {
|
||||
@Override
|
||||
public void onError(IOException ioe) {
|
||||
}
|
||||
}; // Default null cb
|
||||
|
||||
@@ -24,6 +24,7 @@ public class ConnectionException
|
||||
extends SSHException {
|
||||
|
||||
public static final ExceptionChainer<ConnectionException> chainer = new ExceptionChainer<ConnectionException>() {
|
||||
@Override
|
||||
public ConnectionException chain(Throwable t) {
|
||||
if (t instanceof ConnectionException)
|
||||
return (ConnectionException) t;
|
||||
|
||||
@@ -63,19 +63,23 @@ public class ConnectionImpl
|
||||
super("ssh-connection", trans);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attach(Channel chan) {
|
||||
log.info("Attaching `{}` channel (#{})", chan.getType(), chan.getID());
|
||||
channels.put(chan.getID(), chan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel get(int id) {
|
||||
return channels.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForwardedChannelOpener get(String chanType) {
|
||||
return openers.get(chanType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forget(Channel chan) {
|
||||
log.info("Forgetting `{}` channel (#{})", chan.getType(), chan.getID());
|
||||
channels.remove(chan.getID());
|
||||
@@ -85,11 +89,13 @@ public class ConnectionImpl
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forget(ForwardedChannelOpener opener) {
|
||||
log.info("Forgetting opener for `{}` channels: {}", opener.getChannelType(), opener);
|
||||
openers.remove(opener.getChannelType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attach(ForwardedChannelOpener opener) {
|
||||
log.info("Attaching opener for `{}` channels: {}", opener.getChannelType(), opener);
|
||||
openers.put(opener.getChannelType(), opener);
|
||||
@@ -146,26 +152,32 @@ public class ConnectionImpl
|
||||
channels.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPacketSize() {
|
||||
return maxPacketSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transport getTransport() {
|
||||
return trans;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxPacketSize(int maxPacketSize) {
|
||||
this.maxPacketSize = maxPacketSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWindowSize() {
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindowSize(int windowSize) {
|
||||
this.windowSize = windowSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void join()
|
||||
throws InterruptedException {
|
||||
synchronized (internalSynchronizer) {
|
||||
@@ -174,10 +186,12 @@ public class ConnectionImpl
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextID() {
|
||||
return nextID.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
|
||||
byte[] specifics)
|
||||
throws TransportException {
|
||||
@@ -221,6 +235,7 @@ public class ConnectionImpl
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendOpenFailure(int recipient, Reason reason, String message)
|
||||
throws TransportException {
|
||||
trans.write(new SSHPacket(Message.CHANNEL_OPEN_FAILURE)
|
||||
|
||||
@@ -78,7 +78,7 @@ public abstract class AbstractChannel
|
||||
|
||||
private final Queue<Event<ConnectionException>> chanReqResponseEvents = new LinkedList<Event<ConnectionException>>();
|
||||
|
||||
/* The lock used by #newEvent to create open & close events */
|
||||
/* The lock used by to create the open & close events */
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
/** Channel open event */
|
||||
protected final Event<ConnectionException> open;
|
||||
@@ -125,46 +125,57 @@ public abstract class AbstractChannel
|
||||
log.info("Initialized - {}", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAutoExpand() {
|
||||
return autoExpand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalMaxPacketSize() {
|
||||
return lwin.getMaxPacketSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalWinSize() {
|
||||
return lwin.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecipient() {
|
||||
return recipient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemoteMaxPacketSize() {
|
||||
return rwin.getMaxPacketSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemoteWinSize() {
|
||||
return rwin.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Message msg, SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
switch (msg) {
|
||||
@@ -223,6 +234,7 @@ public abstract class AbstractChannel
|
||||
IOUtils.closeQuietly(in, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyError(SSHException error) {
|
||||
log.debug("Channel #{} got notified of {}", getID(), error.toString());
|
||||
|
||||
@@ -235,10 +247,12 @@ public abstract class AbstractChannel
|
||||
finishOff();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoExpand(boolean autoExpand) {
|
||||
this.autoExpand = autoExpand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
throws ConnectionException, TransportException {
|
||||
lock.lock();
|
||||
@@ -267,6 +281,7 @@ public abstract class AbstractChannel
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isOpen() {
|
||||
lock.lock();
|
||||
try {
|
||||
@@ -371,6 +386,7 @@ public abstract class AbstractChannel
|
||||
in.eof();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void sendEOF()
|
||||
throws TransportException {
|
||||
try {
|
||||
|
||||
@@ -34,13 +34,12 @@ public interface Channel
|
||||
/**
|
||||
* Request opening this channel from remote end.
|
||||
*
|
||||
* @throws OpenFailException in case the channel open request was rejected
|
||||
* @throws net.schmizz.sshj.connection.ConnectionException
|
||||
* other connection-layer error
|
||||
* @throws TransportException error writing packets etc.
|
||||
* @throws OpenFailException in case the channel open request was rejected
|
||||
* @throws ConnectionException other connection-layer error
|
||||
* @throws TransportException error writing packets etc.
|
||||
*/
|
||||
void open()
|
||||
throws OpenFailException, ConnectionException, TransportException;
|
||||
throws ConnectionException, TransportException;
|
||||
|
||||
}
|
||||
|
||||
@@ -77,6 +76,7 @@ public interface Channel
|
||||
|
||||
|
||||
/** Close this channel. */
|
||||
@Override
|
||||
void close()
|
||||
throws TransportException, ConnectionException;
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ public final class ChannelInputStream
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void notifyError(SSHException error) {
|
||||
this.error = error;
|
||||
eof();
|
||||
@@ -109,7 +110,7 @@ public final class ChannelInputStream
|
||||
public int read()
|
||||
throws IOException {
|
||||
synchronized (b) {
|
||||
return read(b, 0, 1) == -1 ? -1 : b[0];
|
||||
return read(b, 0, 1) == -1 ? -1 : b[0] & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public final class ChannelOutputStream
|
||||
implements ErrorNotifiable {
|
||||
|
||||
private final Channel chan;
|
||||
private Transport trans;
|
||||
private final Transport trans;
|
||||
private final Window.Remote win;
|
||||
private final SSHPacket buffer = new SSHPacket();
|
||||
private final byte[] b = new byte[1];
|
||||
@@ -102,6 +102,7 @@ public final class ChannelOutputStream
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void notifyError(SSHException error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package net.schmizz.sshj.connection.channel;
|
||||
|
||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||
import net.schmizz.sshj.connection.ConnectionException;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -105,8 +104,7 @@ public abstract class Window {
|
||||
threshold = Math.min(maxPacketSize * 20, initialSize / 4);
|
||||
}
|
||||
|
||||
public int neededAdjustment()
|
||||
throws TransportException {
|
||||
public int neededAdjustment() {
|
||||
synchronized (lock) {
|
||||
return (size - threshold <= 0) ? (initialSize - size) : 0;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public abstract class AbstractDirectChannel
|
||||
conn.attach(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open()
|
||||
throws ConnectionException, TransportException {
|
||||
trans.write(buildOpenReq());
|
||||
|
||||
@@ -48,31 +48,32 @@ public class LocalPortForwarder {
|
||||
|
||||
final ErrorCallback closer = StreamCopier.closeOnErrorCallback(this,
|
||||
new Closeable() {
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException {
|
||||
sock.close();
|
||||
}
|
||||
});
|
||||
|
||||
new StreamCopier("chan2soc", getInputStream(), sock.getOutputStream()) //
|
||||
.bufSize(getLocalMaxPacketSize()) //
|
||||
.errorCallback(closer) //
|
||||
.daemon(true) //
|
||||
new StreamCopier("chan2soc", getInputStream(), sock.getOutputStream())
|
||||
.bufSize(getLocalMaxPacketSize())
|
||||
.errorCallback(closer)
|
||||
.daemon(true)
|
||||
.start();
|
||||
|
||||
new StreamCopier("soc2chan", sock.getInputStream(), getOutputStream()) //
|
||||
.bufSize(getRemoteMaxPacketSize()) //
|
||||
.errorCallback(closer) //
|
||||
.daemon(true) //
|
||||
new StreamCopier("soc2chan", sock.getInputStream(), getOutputStream())
|
||||
.bufSize(getRemoteMaxPacketSize())
|
||||
.errorCallback(closer)
|
||||
.daemon(true)
|
||||
.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSHPacket buildOpenReq() {
|
||||
return super.buildOpenReq() //
|
||||
.putString(host) //
|
||||
.putInt(port) //
|
||||
.putString(ss.getInetAddress().getHostAddress()) //
|
||||
return super.buildOpenReq()
|
||||
.putString(host)
|
||||
.putInt(port)
|
||||
.putString(ss.getInetAddress().getHostAddress())
|
||||
.putInt(ss.getLocalPort());
|
||||
}
|
||||
|
||||
@@ -85,6 +86,19 @@ public class LocalPortForwarder {
|
||||
private final String host;
|
||||
private final int port;
|
||||
|
||||
/**
|
||||
* Create a local port forwarder with specified binding ({@code listeningAddr}. It does not, however, start
|
||||
* listening unless {@link #listen() explicitly told to}. The {@link javax.net.ServerSocketFactory#getDefault()
|
||||
* default} server socket factory is used.
|
||||
*
|
||||
* @param conn {@link Connection} implementation
|
||||
* @param listeningAddr {@link SocketAddress} this forwarder will listen on, if {@code null} then an ephemeral port
|
||||
* and valid local address will be picked to bind the server socket
|
||||
* @param host what host the SSH server will further forward to
|
||||
* @param port port on {@code toHost}
|
||||
*
|
||||
* @throws IOException if there is an error binding on specified {@code listeningAddr}
|
||||
*/
|
||||
public LocalPortForwarder(Connection conn, SocketAddress listeningAddr, String host, int port)
|
||||
throws IOException {
|
||||
this(ServerSocketFactory.getDefault(), conn, listeningAddr, host, port);
|
||||
@@ -94,6 +108,7 @@ public class LocalPortForwarder {
|
||||
* Create a local port forwarder with specified binding ({@code listeningAddr}. It does not, however, start
|
||||
* listening unless {@link #listen() explicitly told to}.
|
||||
*
|
||||
* @param ssf factory to use for creating the server socket
|
||||
* @param conn {@link Connection} implementation
|
||||
* @param listeningAddr {@link SocketAddress} this forwarder will listen on, if {@code null} then an ephemeral port
|
||||
* and valid local address will be picked to bind the server socket
|
||||
@@ -112,16 +127,21 @@ public class LocalPortForwarder {
|
||||
ss.bind(listeningAddr);
|
||||
}
|
||||
|
||||
/** @return the address to which this forwarder is bound for listening */
|
||||
public SocketAddress getListeningAddress() {
|
||||
return ss.getLocalSocketAddress();
|
||||
}
|
||||
|
||||
/** Start listening for incoming connections and forward to remote host as a channel. */
|
||||
/**
|
||||
* Start listening for incoming connections and forward to remote host as a channel.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void listen()
|
||||
throws IOException {
|
||||
log.info("Listening on {}", ss.getLocalSocketAddress());
|
||||
Socket sock;
|
||||
while (true) {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
sock = ss.accept();
|
||||
log.info("Got connection from {}", sock.getRemoteSocketAddress());
|
||||
DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, sock);
|
||||
|
||||
@@ -46,7 +46,7 @@ import net.schmizz.sshj.transport.TransportException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -56,6 +56,8 @@ public class
|
||||
extends AbstractDirectChannel
|
||||
implements Session, Session.Command, Session.Shell, Session.Subsystem {
|
||||
|
||||
private final ChannelInputStream err = new ChannelInputStream(this, trans, lwin);
|
||||
|
||||
private Integer exitStatus;
|
||||
|
||||
private Signal exitSignal;
|
||||
@@ -64,28 +66,17 @@ public class
|
||||
|
||||
private Boolean canDoFlowControl;
|
||||
|
||||
private ChannelInputStream err = new ChannelInputStream(this, trans, lwin);
|
||||
|
||||
public SessionChannel(Connection conn) {
|
||||
super(conn, "session");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void allocateDefaultPTY()
|
||||
throws ConnectionException, TransportException {
|
||||
// TODO FIXME (maybe?): These modes were originally copied from what SSHD was doing;
|
||||
// and then the echo modes were set to 0 to better serve the PTY example.
|
||||
// Not sure what default PTY modes should be.
|
||||
final Map<PTYMode, Integer> modes = new HashMap<PTYMode, Integer>();
|
||||
modes.put(PTYMode.ISIG, 1);
|
||||
modes.put(PTYMode.ICANON, 1);
|
||||
modes.put(PTYMode.ECHO, 0);
|
||||
modes.put(PTYMode.ECHOE, 0);
|
||||
modes.put(PTYMode.ECHOK, 0);
|
||||
modes.put(PTYMode.ECHONL, 0);
|
||||
modes.put(PTYMode.NOFLSH, 0);
|
||||
allocatePTY("vt100", 0, 0, 0, 0, modes);
|
||||
allocatePTY("vt100", 80, 24, 0, 0, Collections.<PTYMode, Integer>emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void allocatePTY(String term, int cols, int rows, int width, int height, Map<PTYMode, Integer> modes)
|
||||
throws ConnectionException, TransportException {
|
||||
sendChannelRequest(
|
||||
@@ -101,10 +92,12 @@ public class
|
||||
).await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean canDoFlowControl() {
|
||||
return canDoFlowControl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeWindowDimensions(int cols, int rows, int width, int height)
|
||||
throws TransportException {
|
||||
sendChannelRequest(
|
||||
@@ -118,6 +111,7 @@ public class
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command exec(String command)
|
||||
throws ConnectionException, TransportException {
|
||||
log.info("Will request to exec `{}`", command);
|
||||
@@ -126,27 +120,33 @@ public class
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorAsString()
|
||||
throws IOException {
|
||||
return StreamCopier.copyStreamToString(err);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getErrorStream() {
|
||||
return err;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExitErrorMessage() {
|
||||
return exitErrMsg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Signal getExitSignal() {
|
||||
return exitSignal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getExitStatus() {
|
||||
return exitStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOutputAsString()
|
||||
throws IOException {
|
||||
return StreamCopier.copyStreamToString(getInputStream());
|
||||
@@ -168,6 +168,7 @@ public class
|
||||
super.handleRequest(req, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reqX11Forwarding(String authProto, String authCookie, int screen)
|
||||
throws ConnectionException,
|
||||
TransportException {
|
||||
@@ -182,23 +183,27 @@ public class
|
||||
).await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvVar(String name, String value)
|
||||
throws ConnectionException, TransportException {
|
||||
sendChannelRequest("env", true, new Buffer.PlainBuffer().putString(name).putString(value))
|
||||
.await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void signal(Signal sig)
|
||||
throws TransportException {
|
||||
sendChannelRequest("signal", false, new Buffer.PlainBuffer().putString(sig.toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shell startShell()
|
||||
throws ConnectionException, TransportException {
|
||||
sendChannelRequest("shell", true, null).await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subsystem startSubsystem(String name)
|
||||
throws ConnectionException, TransportException {
|
||||
log.info("Will request `{}` subsystem", name);
|
||||
@@ -207,6 +212,7 @@ public class
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getExitWasCoreDumped() {
|
||||
return wasCoreDumped;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ public abstract class AbstractForwardedChannel
|
||||
init(recipient, remoteWinSize, remoteMaxPacketSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void confirm()
|
||||
throws TransportException {
|
||||
log.info("Confirming `{}` channel #{}", getType(), getID());
|
||||
@@ -74,16 +75,19 @@ public abstract class AbstractForwardedChannel
|
||||
open.set();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reject(Reason reason, String message)
|
||||
throws TransportException {
|
||||
log.info("Rejecting `{}` channel: {}", getType(), message);
|
||||
conn.sendOpenFailure(getRecipient(), reason, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOriginatorIP() {
|
||||
return origIP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOriginatorPort() {
|
||||
return origPort;
|
||||
}
|
||||
|
||||
@@ -40,14 +40,12 @@ public abstract class AbstractForwardedChannelOpener
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelType() {
|
||||
return chanType;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls the listener with the new channel in a separate thread.
|
||||
*/
|
||||
|
||||
/** Calls the listener with the new channel in a separate thread. */
|
||||
protected void callListener(final ConnectListener listener, final Channel.Forwarded chan) {
|
||||
new Thread() {
|
||||
|
||||
|
||||
@@ -128,13 +128,12 @@ public class RemotePortForwarder
|
||||
private final Forward fwd;
|
||||
|
||||
public ForwardedTCPIPChannel(Connection conn, int recipient, int remoteWinSize, int remoteMaxPacketSize,
|
||||
Forward fwd, String origIP, int origPort)
|
||||
throws TransportException {
|
||||
Forward fwd, String origIP, int origPort) {
|
||||
super(conn, TYPE, recipient, remoteWinSize, remoteMaxPacketSize, origIP, origPort);
|
||||
this.fwd = fwd;
|
||||
}
|
||||
|
||||
/** Returns the forwarding from which this channel originates. */
|
||||
/** @return the forwarding from which this channel originates. */
|
||||
public Forward getParentForward() {
|
||||
return fwd;
|
||||
}
|
||||
@@ -209,6 +208,7 @@ public class RemotePortForwarder
|
||||
* Internal API. Creates a {@link ForwardedTCPIPChannel} from the {@code CHANNEL_OPEN} request and calls associated
|
||||
* {@code ConnectListener} for that forward in a separate thread.
|
||||
*/
|
||||
@Override
|
||||
public void handleOpen(SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
final ForwardedTCPIPChannel chan = new ForwardedTCPIPChannel(conn, buf.readInt(), buf.readInt(), buf.readInt(),
|
||||
|
||||
@@ -40,6 +40,7 @@ public class SocketForwardingConnectListener
|
||||
}
|
||||
|
||||
/** On connect, confirm the channel and start forwarding. */
|
||||
@Override
|
||||
public void gotConnect(Channel.Forwarded chan)
|
||||
throws IOException {
|
||||
log.info("New connection from " + chan.getOriginatorIP() + ":" + chan.getOriginatorPort());
|
||||
@@ -54,6 +55,7 @@ public class SocketForwardingConnectListener
|
||||
chan.confirm();
|
||||
|
||||
final ErrorCallback closer = StreamCopier.closeOnErrorCallback(chan, new Closeable() {
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException {
|
||||
sock.close();
|
||||
|
||||
@@ -52,10 +52,13 @@ public class X11Forwarder
|
||||
}
|
||||
|
||||
/** Internal API */
|
||||
@Override
|
||||
public void handleOpen(SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
callListener(listener, new X11Channel(conn, buf.readInt(), buf.readInt(), buf.readInt(), buf.readString(), buf
|
||||
.readInt()));
|
||||
callListener(listener, new X11Channel(conn,
|
||||
buf.readInt(),
|
||||
buf.readInt(), buf.readInt(),
|
||||
buf.readString(), buf.readInt()));
|
||||
}
|
||||
|
||||
/** Stop handling {@code x11} channel open requests. De-registers itself with connection layer. */
|
||||
|
||||
@@ -25,6 +25,8 @@ import java.util.Set;
|
||||
|
||||
public final class FileAttributes {
|
||||
|
||||
public static final FileAttributes EMPTY = new FileAttributes();
|
||||
|
||||
public static enum Flag {
|
||||
|
||||
SIZE(0x00000001),
|
||||
@@ -58,7 +60,7 @@ public final class FileAttributes {
|
||||
private final long mtime;
|
||||
private final Map<String, String> ext = new HashMap<String, String>();
|
||||
|
||||
public FileAttributes() {
|
||||
private FileAttributes() {
|
||||
size = atime = mtime = uid = gid = mask = 0;
|
||||
mode = new FileMode(0);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.sftp;
|
||||
|
||||
class PathComponents {
|
||||
public class PathComponents {
|
||||
|
||||
public static String adjustForParent(String parent, String path) {
|
||||
return (path.startsWith("/")) ? path // Absolute path, nothing to adjust
|
||||
|
||||
@@ -17,7 +17,7 @@ package net.schmizz.sshj.sftp;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class PathHelper {
|
||||
public class PathHelper {
|
||||
|
||||
private final SFTPEngine sftp;
|
||||
private String dotDir;
|
||||
@@ -49,7 +49,7 @@ class PathHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private String getDotDir()
|
||||
private synchronized String getDotDir()
|
||||
throws IOException {
|
||||
return (dotDir != null) ? dotDir : (dotDir = canon("."));
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ public class RandomAccessRemoteFile
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readBoolean()
|
||||
throws IOException {
|
||||
final int ch = read();
|
||||
@@ -69,6 +70,7 @@ public class RandomAccessRemoteFile
|
||||
return (ch != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte()
|
||||
throws IOException {
|
||||
final int ch = this.read();
|
||||
@@ -77,6 +79,7 @@ public class RandomAccessRemoteFile
|
||||
return (byte) (ch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar()
|
||||
throws IOException {
|
||||
final int ch1 = this.read();
|
||||
@@ -86,21 +89,25 @@ public class RandomAccessRemoteFile
|
||||
return (char) ((ch1 << 8) + ch2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble()
|
||||
throws IOException {
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat()
|
||||
throws IOException {
|
||||
return Float.intBitsToFloat(readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte[] b)
|
||||
throws IOException {
|
||||
readFully(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte[] b, int off, int len)
|
||||
throws IOException {
|
||||
int n = 0;
|
||||
@@ -112,6 +119,7 @@ public class RandomAccessRemoteFile
|
||||
} while (n < len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt()
|
||||
throws IOException {
|
||||
final int ch1 = read();
|
||||
@@ -123,6 +131,7 @@ public class RandomAccessRemoteFile
|
||||
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readLine()
|
||||
throws IOException {
|
||||
StringBuffer input = new StringBuffer();
|
||||
@@ -151,11 +160,13 @@ public class RandomAccessRemoteFile
|
||||
return input.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong()
|
||||
throws IOException {
|
||||
return ((long) (readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort()
|
||||
throws IOException {
|
||||
final int ch1 = this.read();
|
||||
@@ -165,11 +176,13 @@ public class RandomAccessRemoteFile
|
||||
return (short) ((ch1 << 8) + ch2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readUTF()
|
||||
throws IOException {
|
||||
return DataInputStream.readUTF(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedByte()
|
||||
throws IOException {
|
||||
final int ch = this.read();
|
||||
@@ -178,6 +191,7 @@ public class RandomAccessRemoteFile
|
||||
return ch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedShort()
|
||||
throws IOException {
|
||||
final int ch1 = this.read();
|
||||
@@ -187,6 +201,7 @@ public class RandomAccessRemoteFile
|
||||
return (ch1 << 8) + ch2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int skipBytes(int n)
|
||||
throws IOException {
|
||||
if (n <= 0)
|
||||
@@ -202,45 +217,53 @@ public class RandomAccessRemoteFile
|
||||
return (int) (newpos - pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int i)
|
||||
throws IOException {
|
||||
singleByte[0] = (byte) i;
|
||||
write(singleByte);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b)
|
||||
throws IOException {
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len)
|
||||
throws IOException {
|
||||
rf.write(fp, b, off, len);
|
||||
fp += (len - off);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBoolean(boolean v)
|
||||
throws IOException {
|
||||
write(v ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeByte(int v)
|
||||
throws IOException {
|
||||
write(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBytes(String s)
|
||||
throws IOException {
|
||||
final byte[] b = s.getBytes();
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeChar(int v)
|
||||
throws IOException {
|
||||
write((v >>> 8) & 0xFF);
|
||||
write(v & 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeChars(String s)
|
||||
throws IOException {
|
||||
final int clen = s.length();
|
||||
@@ -255,16 +278,19 @@ public class RandomAccessRemoteFile
|
||||
write(b, 0, blen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDouble(double v)
|
||||
throws IOException {
|
||||
writeLong(Double.doubleToLongBits(v));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFloat(float v)
|
||||
throws IOException {
|
||||
writeInt(Float.floatToIntBits(v));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeInt(int v)
|
||||
throws IOException {
|
||||
write((v >>> 24) & 0xFF);
|
||||
@@ -273,6 +299,7 @@ public class RandomAccessRemoteFile
|
||||
write(v & 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLong(long v)
|
||||
throws IOException {
|
||||
write((int) (v >>> 56) & 0xFF);
|
||||
@@ -285,12 +312,14 @@ public class RandomAccessRemoteFile
|
||||
write((int) v & 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeShort(int v)
|
||||
throws IOException {
|
||||
write((v >>> 8) & 0xFF);
|
||||
write(v & 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeUTF(String str)
|
||||
throws IOException {
|
||||
final DataOutputStream dos = new DataOutputStream(rf.new RemoteFileOutputStream(fp));
|
||||
|
||||
@@ -46,7 +46,7 @@ public class RemoteDirectory
|
||||
if (!(name.equals(".") || name.equals("..")) && (filter == null || filter.accept(inf)))
|
||||
rri.add(inf);
|
||||
}
|
||||
break loop;
|
||||
break;
|
||||
|
||||
case STATUS:
|
||||
res.ensureStatusIs(StatusCode.EOF);
|
||||
|
||||
@@ -38,8 +38,8 @@ public class RemoteFile
|
||||
|
||||
public FileAttributes fetchAttributes()
|
||||
throws IOException {
|
||||
return requester.doRequest(newRequest(PacketType.FSTAT)) //
|
||||
.ensurePacketTypeIs(PacketType.ATTRS) //
|
||||
return requester.doRequest(newRequest(PacketType.FSTAT))
|
||||
.ensurePacketTypeIs(PacketType.ATTRS)
|
||||
.readFileAttributes();
|
||||
}
|
||||
|
||||
@@ -73,11 +73,10 @@ public class RemoteFile
|
||||
|
||||
public void write(long fileOffset, byte[] data, int off, int len)
|
||||
throws IOException {
|
||||
requester.doRequest( //
|
||||
newRequest(PacketType.WRITE) //
|
||||
.putUINT64(fileOffset) //
|
||||
.putInt(len - off) //
|
||||
.putRawBytes(data, off, len) //
|
||||
requester.doRequest(newRequest(PacketType.WRITE)
|
||||
.putUINT64(fileOffset)
|
||||
.putInt(len - off)
|
||||
.putRawBytes(data, off, len)
|
||||
).ensureStatusPacketIsOK();
|
||||
}
|
||||
|
||||
@@ -171,7 +170,7 @@ public class RemoteFile
|
||||
@Override
|
||||
public int read()
|
||||
throws IOException {
|
||||
return read(b, 0, 1) == -1 ? -1 : b[0];
|
||||
return read(b, 0, 1) == -1 ? -1 : b[0] & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -45,6 +45,7 @@ abstract class RemoteResource
|
||||
return requester.newRequest(type).putString(handle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException {
|
||||
log.info("Closing `{}`", this);
|
||||
|
||||
@@ -55,12 +55,7 @@ public class RemoteResourceInfo {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof RemoteResourceInfo) {
|
||||
final RemoteResourceInfo that = (RemoteResourceInfo) o;
|
||||
if (comps.equals(that.comps))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return o instanceof RemoteResourceInfo && (comps.equals(((RemoteResourceInfo) o).comps));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -25,11 +25,10 @@ public class Request
|
||||
private final Future<Response, SFTPException> responseFuture;
|
||||
|
||||
public Request(PacketType type, long reqID) {
|
||||
super();
|
||||
this.reqID = reqID;
|
||||
super(type);
|
||||
this.type = type;
|
||||
this.reqID = reqID;
|
||||
responseFuture = new Future<Response, SFTPException>("sftp / " + reqID, SFTPException.chainer);
|
||||
putByte(type.toByte());
|
||||
putInt(reqID);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Deque;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -32,10 +34,12 @@ public class SFTPClient {
|
||||
|
||||
private final SFTPEngine sftp;
|
||||
private final SFTPFileTransfer xfer;
|
||||
private final PathHelper pathHelper;
|
||||
|
||||
public SFTPClient(SessionFactory ssh)
|
||||
throws IOException {
|
||||
this.sftp = new SFTPEngine(ssh).init();
|
||||
this.pathHelper = new PathHelper(sftp);
|
||||
this.xfer = new SFTPFileTransfer(sftp);
|
||||
}
|
||||
|
||||
@@ -70,7 +74,7 @@ public class SFTPClient {
|
||||
|
||||
public RemoteFile open(String filename, Set<OpenMode> mode)
|
||||
throws IOException {
|
||||
return open(filename, mode, new FileAttributes());
|
||||
return open(filename, mode, FileAttributes.EMPTY);
|
||||
}
|
||||
|
||||
public RemoteFile open(String filename)
|
||||
@@ -83,6 +87,38 @@ public class SFTPClient {
|
||||
sftp.makeDir(dirname);
|
||||
}
|
||||
|
||||
public void mkdirs(String path)
|
||||
throws IOException {
|
||||
final Deque<String> dirsToMake = new LinkedList<String>();
|
||||
for (PathComponents current = pathHelper.getComponents(path); ; current = pathHelper
|
||||
.getComponents(current.getParent())) {
|
||||
final FileAttributes attrs = statExistence(current.getPath());
|
||||
if (attrs == null) {
|
||||
dirsToMake.push(current.getPath());
|
||||
} else if (attrs.getType() != FileMode.Type.DIRECTORY) {
|
||||
throw new SFTPException(current.getPath() + " exists but is not a directory");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (!dirsToMake.isEmpty()) {
|
||||
mkdir(dirsToMake.pop());
|
||||
}
|
||||
}
|
||||
|
||||
public FileAttributes statExistence(String path)
|
||||
throws IOException {
|
||||
try {
|
||||
return sftp.stat(path);
|
||||
} catch (SFTPException sftpe) {
|
||||
if (sftpe.getStatusCode() == Response.StatusCode.NO_SUCH_FILE) {
|
||||
return null;
|
||||
} else {
|
||||
throw sftpe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void rename(String oldpath, String newpath)
|
||||
throws IOException {
|
||||
sftp.rename(oldpath, newpath);
|
||||
|
||||
@@ -87,10 +87,12 @@ public class SFTPEngine
|
||||
return negotiatedVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Request newRequest(PacketType type) {
|
||||
return new Request(type, reqID = reqID + 1 & 0xffffffffL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response doRequest(Request req)
|
||||
throws IOException {
|
||||
reader.expectResponseTo(req);
|
||||
@@ -112,7 +114,6 @@ public class SFTPEngine
|
||||
|
||||
public RemoteFile open(String path, Set<OpenMode> modes, FileAttributes fa)
|
||||
throws IOException {
|
||||
|
||||
final String handle = doRequest(
|
||||
newRequest(PacketType.OPEN).putString(path).putInt(OpenMode.toMask(modes)).putFileAttributes(fa)
|
||||
).ensurePacketTypeIs(PacketType.HANDLE).readString();
|
||||
@@ -121,7 +122,7 @@ public class SFTPEngine
|
||||
|
||||
public RemoteFile open(String filename, Set<OpenMode> modes)
|
||||
throws IOException {
|
||||
return open(filename, modes, new FileAttributes());
|
||||
return open(filename, modes, FileAttributes.EMPTY);
|
||||
}
|
||||
|
||||
public RemoteFile open(String filename)
|
||||
@@ -154,14 +155,12 @@ public class SFTPEngine
|
||||
|
||||
public void makeDir(String path, FileAttributes attrs)
|
||||
throws IOException {
|
||||
doRequest(
|
||||
newRequest(PacketType.MKDIR).putString(path).putFileAttributes(attrs)
|
||||
).ensureStatusPacketIsOK();
|
||||
doRequest(newRequest(PacketType.MKDIR).putString(path).putFileAttributes(attrs)).ensureStatusPacketIsOK();
|
||||
}
|
||||
|
||||
public void makeDir(String path)
|
||||
throws IOException {
|
||||
makeDir(path, new FileAttributes());
|
||||
makeDir(path, FileAttributes.EMPTY);
|
||||
}
|
||||
|
||||
public void symlink(String linkpath, String targetpath)
|
||||
|
||||
@@ -25,6 +25,7 @@ public class SFTPException
|
||||
|
||||
public static final ExceptionChainer<SFTPException> chainer = new ExceptionChainer<SFTPException>() {
|
||||
|
||||
@Override
|
||||
public SFTPException chain(Throwable t) {
|
||||
if (t instanceof SFTPException)
|
||||
return (SFTPException) t;
|
||||
|
||||
@@ -20,6 +20,7 @@ import net.schmizz.sshj.sftp.Response.StatusCode;
|
||||
import net.schmizz.sshj.xfer.AbstractFileTransfer;
|
||||
import net.schmizz.sshj.xfer.FileTransfer;
|
||||
import net.schmizz.sshj.xfer.FileTransferUtil;
|
||||
import net.schmizz.sshj.xfer.TransferListener;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
@@ -39,12 +40,14 @@ public class SFTPFileTransfer
|
||||
private volatile RemoteResourceFilter downloadFilter = defaultRemoteFilter;
|
||||
|
||||
private static final FileFilter defaultLocalFilter = new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathName) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private static final RemoteResourceFilter defaultRemoteFilter = new RemoteResourceFilter() {
|
||||
@Override
|
||||
public boolean accept(RemoteResourceInfo resource) {
|
||||
return true;
|
||||
}
|
||||
@@ -55,11 +58,13 @@ public class SFTPFileTransfer
|
||||
this.pathHelper = new PathHelper(sftp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(String source, String dest)
|
||||
throws IOException {
|
||||
new Uploader().upload(new File(source), dest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(String source, String dest)
|
||||
throws IOException {
|
||||
final PathComponents pathComponents = pathHelper.getComponents(source);
|
||||
@@ -85,7 +90,64 @@ public class SFTPFileTransfer
|
||||
|
||||
private class Downloader {
|
||||
|
||||
private void setAttributes(final RemoteResourceInfo remote, final File local)
|
||||
private final TransferListener listener = getTransferListener();
|
||||
|
||||
private void download(final RemoteResourceInfo remote, final File local)
|
||||
throws IOException {
|
||||
final File adjustedFile;
|
||||
switch (remote.getAttributes().getType()) {
|
||||
case DIRECTORY:
|
||||
listener.startedDir(remote.getName());
|
||||
adjustedFile = downloadDir(remote, local);
|
||||
listener.finishedDir();
|
||||
break;
|
||||
case UNKNOWN:
|
||||
log.warn("Server did not supply information about the type of file at `{}` " +
|
||||
"-- assuming it is a regular file!", remote.getPath());
|
||||
case REGULAR:
|
||||
listener.startedFile(remote.getName(), remote.getAttributes().getSize());
|
||||
adjustedFile = downloadFile(remote, local);
|
||||
listener.finishedFile();
|
||||
break;
|
||||
default:
|
||||
throw new IOException(remote + " is not a regular file or directory");
|
||||
}
|
||||
copyAttributes(remote, adjustedFile);
|
||||
|
||||
}
|
||||
|
||||
private File downloadDir(final RemoteResourceInfo remote, final File local)
|
||||
throws IOException {
|
||||
final File adjusted = FileTransferUtil.getTargetDirectory(local, remote.getName());
|
||||
final RemoteDirectory rd = sftp.openDir(remote.getPath());
|
||||
try {
|
||||
for (RemoteResourceInfo rri : rd.scan(getDownloadFilter()))
|
||||
download(rri, new File(adjusted.getPath(), rri.getName()));
|
||||
} finally {
|
||||
rd.close();
|
||||
}
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
private File downloadFile(final RemoteResourceInfo remote, final File local)
|
||||
throws IOException {
|
||||
final File adjusted = FileTransferUtil.getTargetFile(local, remote.getName());
|
||||
final RemoteFile rf = sftp.open(remote.getPath());
|
||||
try {
|
||||
final FileOutputStream fos = new FileOutputStream(adjusted);
|
||||
try {
|
||||
StreamCopier.copy(rf.getInputStream(), fos, sftp.getSubsystem()
|
||||
.getLocalMaxPacketSize(), false, listener);
|
||||
} finally {
|
||||
fos.close();
|
||||
}
|
||||
} finally {
|
||||
rf.close();
|
||||
}
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
private void copyAttributes(final RemoteResourceInfo remote, final File local)
|
||||
throws IOException {
|
||||
final FileAttributes attrs = remote.getAttributes();
|
||||
getModeSetter().setPermissions(local, attrs.getMode().getPermissionsMask());
|
||||
@@ -95,91 +157,54 @@ public class SFTPFileTransfer
|
||||
}
|
||||
}
|
||||
|
||||
private void downloadFile(final RemoteResourceInfo remote, final File local)
|
||||
throws IOException {
|
||||
final File adjusted = FileTransferUtil.getTargetFile(local, remote.getName());
|
||||
setAttributes(remote, adjusted);
|
||||
final RemoteFile rf = sftp.open(remote.getPath());
|
||||
try {
|
||||
final FileOutputStream fos = new FileOutputStream(adjusted);
|
||||
try {
|
||||
StreamCopier.copy(rf.getInputStream(), fos, sftp.getSubsystem()
|
||||
.getLocalMaxPacketSize(), false);
|
||||
} finally {
|
||||
fos.close();
|
||||
}
|
||||
} finally {
|
||||
rf.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void downloadDir(final RemoteResourceInfo remote, final File local)
|
||||
throws IOException {
|
||||
final File adjusted = FileTransferUtil.getTargetDirectory(local, remote.getName());
|
||||
setAttributes(remote, adjusted);
|
||||
final RemoteDirectory rd = sftp.openDir(remote.getPath());
|
||||
try {
|
||||
for (RemoteResourceInfo rri : rd.scan(getDownloadFilter()))
|
||||
download(rri, new File(adjusted.getPath(), rri.getName()));
|
||||
} finally {
|
||||
rd.close();
|
||||
}
|
||||
}
|
||||
|
||||
void download(final RemoteResourceInfo remote, final File local)
|
||||
throws IOException {
|
||||
log.info("Downloading [{}] to [{}]", remote, local);
|
||||
switch (remote.getAttributes().getType()) {
|
||||
case DIRECTORY:
|
||||
downloadDir(remote, local);
|
||||
break;
|
||||
case UNKNOWN:
|
||||
log.warn("Server did not supply information about the type of file at `{}` -- assuming it is a regular file!");
|
||||
case REGULAR:
|
||||
downloadFile(remote, local);
|
||||
break;
|
||||
default:
|
||||
throw new IOException(remote + " is not a regular file or directory");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Uploader {
|
||||
|
||||
void upload(File local, String remote)
|
||||
private final TransferListener listener = getTransferListener();
|
||||
|
||||
private void upload(File local, String remote)
|
||||
throws IOException {
|
||||
log.info("Uploading [{}] to [{}]", local, remote);
|
||||
if (local.isDirectory())
|
||||
uploadDir(local, remote);
|
||||
else if (local.isFile())
|
||||
uploadFile(local, remote);
|
||||
else
|
||||
final String adjustedPath;
|
||||
if (local.isDirectory()) {
|
||||
listener.startedDir(local.getName());
|
||||
adjustedPath = uploadDir(local, remote);
|
||||
listener.finishedDir();
|
||||
} else if (local.isFile()) {
|
||||
listener.startedFile(local.getName(), local.length());
|
||||
adjustedPath = uploadFile(local, remote);
|
||||
listener.finishedFile();
|
||||
} else
|
||||
throw new IOException(local + " is not a file or directory");
|
||||
sftp.setAttributes(adjustedPath, getAttributes(local));
|
||||
}
|
||||
|
||||
private void uploadDir(File local, String remote)
|
||||
private String uploadDir(File local, String remote)
|
||||
throws IOException {
|
||||
final String adjusted = prepareDir(local, remote);
|
||||
for (File f : local.listFiles(getUploadFilter()))
|
||||
upload(f, adjusted);
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
private void uploadFile(File local, String remote)
|
||||
private String uploadFile(File local, String remote)
|
||||
throws IOException {
|
||||
final String adjusted = prepareFile(local, remote);
|
||||
final RemoteFile rf = sftp.open(adjusted, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT, OpenMode.TRUNC),
|
||||
getAttributes(local));
|
||||
final RemoteFile rf = sftp.open(adjusted, EnumSet.of(OpenMode.WRITE,
|
||||
OpenMode.CREAT,
|
||||
OpenMode.TRUNC));
|
||||
try {
|
||||
final FileInputStream fis = new FileInputStream(local);
|
||||
try {
|
||||
StreamCopier.copy(fis, rf.getOutputStream(), sftp.getSubsystem().getRemoteMaxPacketSize()
|
||||
- rf.getOutgoingPacketOverhead(), false);
|
||||
final int bufSize = sftp.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead();
|
||||
StreamCopier.copy(fis, rf.getOutputStream(), bufSize, false, listener);
|
||||
} finally {
|
||||
fis.close();
|
||||
}
|
||||
} finally {
|
||||
rf.close();
|
||||
}
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
private String prepareDir(File local, String remote)
|
||||
@@ -190,7 +215,7 @@ public class SFTPFileTransfer
|
||||
} catch (SFTPException e) {
|
||||
if (e.getStatusCode() == StatusCode.NO_SUCH_FILE) {
|
||||
log.debug("probeDir: {} does not exist, creating", remote);
|
||||
sftp.makeDir(remote, getAttributes(local));
|
||||
sftp.makeDir(remote);
|
||||
return remote;
|
||||
} else
|
||||
throw e;
|
||||
@@ -199,11 +224,6 @@ public class SFTPFileTransfer
|
||||
if (attrs.getMode().getType() == FileMode.Type.DIRECTORY)
|
||||
if (pathHelper.getComponents(remote).getName().equals(local.getName())) {
|
||||
log.debug("probeDir: {} already exists", remote);
|
||||
final FileAttributes localAttrs = getAttributes(local);
|
||||
if (attrs.getMode().getMask() != localAttrs.getMode().getMask()
|
||||
|| (getModeGetter().preservesTimes()
|
||||
&& (attrs.getAtime() != attrs.getAtime() || attrs.getMtime() != localAttrs.getMtime())))
|
||||
sftp.setAttributes(remote, localAttrs);
|
||||
return remote;
|
||||
} else {
|
||||
log.debug("probeDir: {} already exists, path adjusted for {}", remote, local.getName());
|
||||
@@ -238,7 +258,6 @@ public class SFTPFileTransfer
|
||||
private FileAttributes getAttributes(File local)
|
||||
throws IOException {
|
||||
final FileAttributes.Builder builder = new FileAttributes.Builder()
|
||||
.withType(local.isDirectory() ? FileMode.Type.DIRECTORY : FileMode.Type.REGULAR)
|
||||
.withPermissions(getModeGetter().getPermissions(local));
|
||||
if (getModeGetter().preservesTimes())
|
||||
builder.withAtimeMtime(getModeGetter().getLastAccessTime(local), getModeGetter().getLastModifiedTime(local));
|
||||
|
||||
@@ -37,8 +37,12 @@ public class StatefulSFTPClient
|
||||
return PathComponents.adjustForParent(cwd, path);
|
||||
}
|
||||
|
||||
public synchronized void cd(String dirname) {
|
||||
public synchronized void cd(String dirname)
|
||||
throws IOException {
|
||||
cwd = cwdify(dirname);
|
||||
if (statExistence(cwd) == null) {
|
||||
throw new SFTPException(cwd + ": does not exist");
|
||||
}
|
||||
log.info("CWD = " + cwd);
|
||||
}
|
||||
|
||||
@@ -98,6 +102,18 @@ public class StatefulSFTPClient
|
||||
super.mkdir(cwdify(dirname));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mkdirs(String path)
|
||||
throws IOException {
|
||||
super.mkdirs(cwdify(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileAttributes statExistence(String path)
|
||||
throws IOException {
|
||||
return super.statExistence(cwdify(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String oldpath, String newpath)
|
||||
throws IOException {
|
||||
|
||||
@@ -47,29 +47,32 @@ import java.security.SignatureException;
|
||||
public abstract class AbstractSignature
|
||||
implements Signature {
|
||||
|
||||
protected final String algorithm;
|
||||
protected java.security.Signature signature;
|
||||
protected String algorithm;
|
||||
|
||||
protected AbstractSignature(String algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
public void init(PublicKey pubkey, PrivateKey prvkey) {
|
||||
@Override
|
||||
public void init(PublicKey publicKey, PrivateKey privateKey) {
|
||||
try {
|
||||
signature = SecurityUtils.getSignature(algorithm);
|
||||
if (pubkey != null)
|
||||
signature.initVerify(pubkey);
|
||||
if (prvkey != null)
|
||||
signature.initSign(prvkey);
|
||||
if (publicKey != null)
|
||||
signature.initVerify(publicKey);
|
||||
if (privateKey != null)
|
||||
signature.initSign(privateKey);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] foo) {
|
||||
update(foo, 0, foo.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] foo, int off, int len) {
|
||||
try {
|
||||
signature.update(foo, off, len);
|
||||
@@ -81,19 +84,18 @@ public abstract class AbstractSignature
|
||||
protected byte[] extractSig(byte[] sig) {
|
||||
if (sig[0] == 0 && sig[1] == 0 && sig[2] == 0) {
|
||||
int i = 0;
|
||||
int j;
|
||||
j = sig[i++] << 24 & 0xff000000 //
|
||||
| sig[i++] << 16 & 0x00ff0000 //
|
||||
| sig[i++] << 8 & 0x0000ff00 //
|
||||
| sig[i++] & 0x000000ff;
|
||||
int j = sig[i++] << 24 & 0xff000000
|
||||
| sig[i++] << 16 & 0x00ff0000
|
||||
| sig[i++] << 8 & 0x0000ff00
|
||||
| sig[i++] & 0x000000ff;
|
||||
i += j;
|
||||
j = sig[i++] << 24 & 0xff000000 //
|
||||
| sig[i++] << 16 & 0x00ff0000 //
|
||||
| sig[i++] << 8 & 0x0000ff00 //
|
||||
j = sig[i++] << 24 & 0xff000000
|
||||
| sig[i++] << 16 & 0x00ff0000
|
||||
| sig[i++] << 8 & 0x0000ff00
|
||||
| sig[i++] & 0x000000ff;
|
||||
byte[] tmp = new byte[j];
|
||||
System.arraycopy(sig, i, tmp, 0, j);
|
||||
sig = tmp;
|
||||
byte[] newSig = new byte[j];
|
||||
System.arraycopy(sig, i, newSig, 0, j);
|
||||
sig = newSig;
|
||||
}
|
||||
return sig;
|
||||
}
|
||||
|
||||
@@ -48,10 +48,12 @@ public class SignatureDSA
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||
|
||||
@Override
|
||||
public Signature create() {
|
||||
return new SignatureDSA();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return KeyType.DSA.toString();
|
||||
}
|
||||
@@ -62,6 +64,7 @@ public class SignatureDSA
|
||||
super("SHA1withDSA");
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sign() {
|
||||
byte[] sig;
|
||||
try {
|
||||
@@ -72,15 +75,16 @@ public class SignatureDSA
|
||||
|
||||
// sig is in ASN.1
|
||||
// SEQUENCE::={ r INTEGER, s INTEGER }
|
||||
int len = 0;
|
||||
int index = 3;
|
||||
len = sig[index++] & 0xff;
|
||||
byte[] r = new byte[len];
|
||||
System.arraycopy(sig, index, r, 0, r.length);
|
||||
index = index + len + 1;
|
||||
len = sig[index++] & 0xff;
|
||||
byte[] s = new byte[len];
|
||||
System.arraycopy(sig, index, s, 0, s.length);
|
||||
|
||||
int rIndex = 3;
|
||||
int rLen = sig[rIndex++] & 0xff;
|
||||
byte[] r = new byte[rLen];
|
||||
System.arraycopy(sig, rIndex, r, 0, r.length);
|
||||
|
||||
int sIndex = rIndex + rLen + 1;
|
||||
int sLen = sig[sIndex++] & 0xff;
|
||||
byte[] s = new byte[sLen];
|
||||
System.arraycopy(sig, sIndex, s, 0, s.length);
|
||||
|
||||
byte[] result = new byte[40];
|
||||
|
||||
@@ -91,6 +95,7 @@ public class SignatureDSA
|
||||
result,
|
||||
r.length > 20 ? 0 : 20 - r.length,
|
||||
r.length > 20 ? 20 : r.length);
|
||||
|
||||
System.arraycopy(s,
|
||||
s.length > 20 ? 1 : 0,
|
||||
result,
|
||||
@@ -100,6 +105,7 @@ public class SignatureDSA
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(byte[] sig) {
|
||||
sig = extractSig(sig);
|
||||
|
||||
|
||||
@@ -48,10 +48,12 @@ public class SignatureRSA
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||
|
||||
@Override
|
||||
public Signature create() {
|
||||
return new SignatureRSA();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return KeyType.RSA.toString();
|
||||
}
|
||||
@@ -62,6 +64,7 @@ public class SignatureRSA
|
||||
super("SHA1withRSA");
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sign() {
|
||||
try {
|
||||
return signature.sign();
|
||||
@@ -70,6 +73,7 @@ public class SignatureRSA
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(byte[] sig) {
|
||||
sig = extractSig(sig);
|
||||
try {
|
||||
|
||||
@@ -74,7 +74,7 @@ abstract class Converter {
|
||||
this.mac = mac;
|
||||
this.compression = compression;
|
||||
if (compression != null)
|
||||
compression.init(getCompressionType(), -1);
|
||||
compression.init(getCompressionType());
|
||||
this.cipherSize = cipher.getIVSize();
|
||||
}
|
||||
|
||||
@@ -86,6 +86,6 @@ abstract class Converter {
|
||||
return compression != null && (authed || !compression.isDelayed());
|
||||
}
|
||||
|
||||
abstract Compression.Type getCompressionType();
|
||||
abstract Compression.Mode getCompressionType();
|
||||
|
||||
}
|
||||
@@ -199,8 +199,8 @@ final class Decoder
|
||||
}
|
||||
|
||||
@Override
|
||||
Compression.Type getCompressionType() {
|
||||
return Compression.Type.Inflater;
|
||||
Compression.Mode getCompressionType() {
|
||||
return Compression.Mode.INFLATE;
|
||||
}
|
||||
|
||||
int getMaxPacketLength() {
|
||||
|
||||
@@ -160,9 +160,8 @@ final class Encoder
|
||||
}
|
||||
|
||||
@Override
|
||||
Compression.Type getCompressionType() {
|
||||
return Compression.Type.Deflater;
|
||||
Compression.Mode getCompressionType() {
|
||||
return Compression.Mode.DEFLATE;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -47,6 +47,7 @@ import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
@@ -228,8 +229,12 @@ final class KeyExchanger
|
||||
log.debug("Negotiated algorithms: {}", negotiatedAlgs);
|
||||
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(), negotiatedAlgs
|
||||
.getKeyExchangeAlgorithm());
|
||||
kex.init(transport, transport.getServerID().getBytes(), transport.getClientID().getBytes(), buf
|
||||
.getCompactData(), clientProposal.getPacket().getCompactData());
|
||||
try {
|
||||
kex.init(transport, transport.getServerID().getBytes(), transport.getClientID().getBytes(), buf
|
||||
.getCompactData(), clientProposal.getPacket().getCompactData());
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,6 +331,7 @@ final class KeyExchanger
|
||||
transport.getDecoder().setAlgorithms(cipher_S2C, mac_S2C, compression_S2C);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Message msg, SSHPacket buf)
|
||||
throws TransportException {
|
||||
switch (expected) {
|
||||
@@ -347,10 +353,14 @@ final class KeyExchanger
|
||||
case FOLLOWUP:
|
||||
ensureKexOngoing();
|
||||
log.info("Received kex followup data");
|
||||
if (kex.next(msg, buf)) {
|
||||
verifyHost(kex.getHostKey());
|
||||
sendNewKeys();
|
||||
expected = Expected.NEWKEYS;
|
||||
try {
|
||||
if (kex.next(msg, buf)) {
|
||||
verifyHost(kex.getHostKey());
|
||||
sendNewKeys();
|
||||
expected = Expected.NEWKEYS;
|
||||
}
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, e);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -369,6 +379,7 @@ final class KeyExchanger
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyError(SSHException error) {
|
||||
log.debug("Got notified of {}", error.toString());
|
||||
FutureUtils.alertAll(error, kexInitSent, done);
|
||||
|
||||
@@ -25,6 +25,7 @@ public class TransportException
|
||||
|
||||
/** @see ExceptionChainer */
|
||||
public static final ExceptionChainer<TransportException> chainer = new ExceptionChainer<TransportException>() {
|
||||
@Override
|
||||
public TransportException chain(Throwable t) {
|
||||
if (t instanceof TransportException)
|
||||
return (TransportException) t;
|
||||
|
||||
@@ -133,6 +133,7 @@ public final class TransportImpl
|
||||
clientID = "SSH-2.0-" + config.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(String remoteHost, int remotePort, InputStream in, OutputStream out)
|
||||
throws TransportException {
|
||||
connInfo = new ConnInfo(remoteHost, remotePort, in, out);
|
||||
@@ -214,10 +215,12 @@ public final class TransportImpl
|
||||
return ident;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHostKeyVerifier(HostKeyVerifier hkv) {
|
||||
kexer.addHostKeyVerifier(hkv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doKex()
|
||||
throws TransportException {
|
||||
kexer.startKex(true);
|
||||
@@ -227,50 +230,62 @@ public final class TransportImpl
|
||||
return kexer.isKexDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimeout(int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeartbeatInterval() {
|
||||
return heartbeater.getInterval();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeartbeatInterval(int interval) {
|
||||
heartbeater.setInterval(interval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteHost() {
|
||||
return connInfo.host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemotePort() {
|
||||
return connInfo.port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientVersion() {
|
||||
return clientID.substring(8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerVersion() {
|
||||
return serverID == null ? serverID : serverID.substring(8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSessionID() {
|
||||
return kexer.getSessionID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Service getService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setService(Service service) {
|
||||
if (service == null)
|
||||
service = nullService;
|
||||
@@ -279,6 +294,7 @@ public final class TransportImpl
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reqService(Service service)
|
||||
throws TransportException {
|
||||
serviceAccept.lock();
|
||||
@@ -305,16 +321,19 @@ public final class TransportImpl
|
||||
write(new SSHPacket(Message.SERVICE_REQUEST).putString(serviceName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticated() {
|
||||
this.authed = true;
|
||||
encoder.setAuthenticated();
|
||||
decoder.setAuthenticated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthenticated() {
|
||||
return authed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sendUnimplemented()
|
||||
throws TransportException {
|
||||
final long seq = decoder.getSequenceNumber();
|
||||
@@ -322,23 +341,28 @@ public final class TransportImpl
|
||||
return write(new SSHPacket(Message.UNIMPLEMENTED).putInt(seq));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void join()
|
||||
throws TransportException {
|
||||
close.await();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return reader.isAlive() && !close.isSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
disconnect(DisconnectReason.BY_APPLICATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(DisconnectReason reason) {
|
||||
disconnect(reason, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(DisconnectReason reason, String message) {
|
||||
close.lock(); // CAS type operation on close
|
||||
try {
|
||||
@@ -357,6 +381,7 @@ public final class TransportImpl
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long write(SSHPacket payload)
|
||||
throws TransportException {
|
||||
writeLock.lock();
|
||||
@@ -416,6 +441,7 @@ public final class TransportImpl
|
||||
*
|
||||
* @throws SSHException if an error occurs during handling (unrecoverable)
|
||||
*/
|
||||
@Override
|
||||
public void handle(Message msg, SSHPacket buf)
|
||||
throws SSHException {
|
||||
this.msg = msg;
|
||||
|
||||
@@ -42,10 +42,12 @@ public class AES128CBC
|
||||
/** Named factory for AES128CBC Cipher */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||
@Override
|
||||
public Cipher create() {
|
||||
return new AES128CBC();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "aes128-cbc";
|
||||
}
|
||||
|
||||
@@ -42,10 +42,12 @@ public class AES128CTR
|
||||
/** Named factory for AES128CBC Cipher */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||
@Override
|
||||
public Cipher create() {
|
||||
return new AES128CTR();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "aes128-ctr";
|
||||
}
|
||||
|
||||
@@ -42,10 +42,12 @@ public class AES192CBC
|
||||
/** Named factory for AES192CBC Cipher */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||
@Override
|
||||
public Cipher create() {
|
||||
return new AES192CBC();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "aes192-cbc";
|
||||
}
|
||||
|
||||
@@ -42,10 +42,12 @@ public class AES192CTR
|
||||
/** Named factory for AES192CTR Cipher */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||
@Override
|
||||
public Cipher create() {
|
||||
return new AES192CTR();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "aes192-ctr";
|
||||
}
|
||||
|
||||
@@ -42,10 +42,12 @@ public class AES256CBC
|
||||
/** Named factory for AES256CBC Cipher */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||
@Override
|
||||
public Cipher create() {
|
||||
return new AES256CBC();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "aes256-cbc";
|
||||
}
|
||||
|
||||
@@ -42,10 +42,12 @@ public class AES256CTR
|
||||
/** Named factory for AES256CBC Cipher */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||
@Override
|
||||
public Cipher create() {
|
||||
return new AES256CTR();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "aes256-ctr";
|
||||
}
|
||||
|
||||
@@ -70,14 +70,17 @@ public class BaseCipher
|
||||
this.transformation = transformation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockSize() {
|
||||
return bsize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIVSize() {
|
||||
return ivsize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Mode mode, byte[] key, byte[] iv) {
|
||||
key = BaseCipher.resize(key, bsize);
|
||||
iv = BaseCipher.resize(iv, ivsize);
|
||||
@@ -91,6 +94,7 @@ public class BaseCipher
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] input, int inputOffset, int inputLen) {
|
||||
try {
|
||||
cipher.update(input, inputOffset, inputLen, input, inputOffset);
|
||||
|
||||
@@ -42,10 +42,12 @@ public class BlowfishCBC
|
||||
/** Named factory for BlowfishCBC Cipher */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||
@Override
|
||||
public Cipher create() {
|
||||
return new BlowfishCBC();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "blowfish-cbc";
|
||||
}
|
||||
|
||||
@@ -42,26 +42,32 @@ public class NoneCipher
|
||||
/** Named factory for the no-op Cipher */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||
@Override
|
||||
public Cipher create() {
|
||||
return new NoneCipher();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockSize() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIVSize() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Mode mode, byte[] bytes, byte[] bytes1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] input, int inputOffset, int inputLen) {
|
||||
}
|
||||
|
||||
|
||||
@@ -42,10 +42,12 @@ public class TripleDESCBC
|
||||
/** Named factory for TripleDESCBC Cipher */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||
@Override
|
||||
public Cipher create() {
|
||||
return new TripleDESCBC();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "3des-cbc";
|
||||
}
|
||||
|
||||
@@ -35,34 +35,26 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport.compression;
|
||||
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
|
||||
/** Interface used to compress the stream of data between the SSH server and clients. */
|
||||
public interface Compression {
|
||||
|
||||
/** Enum identifying if this object will be used to compress or uncompress data. */
|
||||
enum Type {
|
||||
Inflater,
|
||||
Deflater
|
||||
enum Mode {
|
||||
INFLATE,
|
||||
DEFLATE
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress the given buffer in place.
|
||||
*
|
||||
* @param buffer the buffer containing the data to compress s
|
||||
*/
|
||||
void compress(SSHPacket buffer);
|
||||
|
||||
/**
|
||||
* Initialize this object to either compress or uncompress data. This method must be called prior to any calls to
|
||||
* either <code>compress</code> or <code>uncompress</code>. Once the object has been initialized, only one of
|
||||
* <code>compress</code> or <code>uncompress</code> method can be called.
|
||||
*
|
||||
* @param type
|
||||
* @param level
|
||||
* @param mode
|
||||
*/
|
||||
void init(Type type, int level);
|
||||
void init(Mode mode);
|
||||
|
||||
/**
|
||||
* Delayed compression is an Open-SSH specific feature which informs both the client and server to not compress data
|
||||
@@ -72,13 +64,22 @@ public interface Compression {
|
||||
*/
|
||||
boolean isDelayed();
|
||||
|
||||
/**
|
||||
* Compress the given buffer in place.
|
||||
*
|
||||
* @param buffer the buffer containing the data to compress s
|
||||
*/
|
||||
void compress(Buffer buffer);
|
||||
|
||||
/**
|
||||
* Uncompress the data in a buffer into another buffer.
|
||||
*
|
||||
* @param from the buffer containing the data to uncompress
|
||||
* @param to the buffer receiving the uncompressed data
|
||||
*
|
||||
* @throws TransportException
|
||||
*/
|
||||
void uncompress(SSHPacket from, SSHPacket to)
|
||||
void uncompress(Buffer from, Buffer to)
|
||||
throws TransportException;
|
||||
|
||||
}
|
||||
|
||||
@@ -46,10 +46,12 @@ public class DelayedZlibCompression
|
||||
/** Named factory for the ZLib Delayed Compression. */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Compression> {
|
||||
@Override
|
||||
public Compression create() {
|
||||
return new DelayedZlibCompression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "zlib@openssh.com";
|
||||
}
|
||||
|
||||
@@ -42,10 +42,12 @@ public abstract class NoneCompression
|
||||
/** Named factory for the no-op <code>Compression</code> */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Compression> {
|
||||
@Override
|
||||
public Compression create() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "none";
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ package net.schmizz.sshj.transport.compression;
|
||||
|
||||
import com.jcraft.jzlib.JZlib;
|
||||
import com.jcraft.jzlib.ZStream;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.DisconnectReason;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
|
||||
@@ -49,73 +49,80 @@ public class ZlibCompression
|
||||
/** Named factory for the ZLib Compression. */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Compression> {
|
||||
@Override
|
||||
public Compression create() {
|
||||
return new ZlibCompression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "zlib";
|
||||
}
|
||||
}
|
||||
|
||||
static private final int BUF_SIZE = 4096;
|
||||
private static final int BUF_SIZE = 4096;
|
||||
|
||||
private final byte[] tempBuf = new byte[BUF_SIZE];
|
||||
|
||||
private ZStream stream;
|
||||
private final byte[] tmpbuf = new byte[BUF_SIZE];
|
||||
|
||||
/** Create a new instance of a ZLib base compression */
|
||||
public ZlibCompression() {
|
||||
@Override
|
||||
public void init(Mode mode) {
|
||||
stream = new ZStream();
|
||||
switch (mode) {
|
||||
case DEFLATE:
|
||||
stream.deflateInit(JZlib.Z_DEFAULT_COMPRESSION);
|
||||
break;
|
||||
case INFLATE:
|
||||
stream.inflateInit();
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
}
|
||||
|
||||
public void compress(SSHPacket buffer) {
|
||||
@Override
|
||||
public boolean isDelayed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compress(Buffer buffer) {
|
||||
stream.next_in = buffer.array();
|
||||
stream.next_in_index = buffer.rpos();
|
||||
stream.avail_in = buffer.available();
|
||||
buffer.wpos(buffer.rpos());
|
||||
do {
|
||||
stream.next_out = tmpbuf;
|
||||
stream.next_out = tempBuf;
|
||||
stream.next_out_index = 0;
|
||||
stream.avail_out = BUF_SIZE;
|
||||
int status = stream.deflate(JZlib.Z_PARTIAL_FLUSH);
|
||||
switch (status) {
|
||||
case JZlib.Z_OK:
|
||||
buffer.putRawBytes(tmpbuf, 0, BUF_SIZE - stream.avail_out);
|
||||
break;
|
||||
default:
|
||||
throw new SSHRuntimeException("compress: deflate returned " + status);
|
||||
final int status = stream.deflate(JZlib.Z_PARTIAL_FLUSH);
|
||||
if (status == JZlib.Z_OK) {
|
||||
buffer.putRawBytes(tempBuf, 0, BUF_SIZE - stream.avail_out);
|
||||
} else {
|
||||
throw new SSHRuntimeException("compress: deflate returned " + status);
|
||||
}
|
||||
} while (stream.avail_out == 0);
|
||||
}
|
||||
|
||||
public void init(Type type, int level) {
|
||||
stream = new ZStream();
|
||||
if (type == Type.Deflater)
|
||||
stream.deflateInit(level);
|
||||
else
|
||||
stream.inflateInit();
|
||||
}
|
||||
|
||||
public boolean isDelayed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void uncompress(SSHPacket from, SSHPacket to)
|
||||
@Override
|
||||
public void uncompress(Buffer from, Buffer to)
|
||||
throws TransportException {
|
||||
stream.next_in = from.array();
|
||||
stream.next_in_index = from.rpos();
|
||||
stream.avail_in = from.available();
|
||||
|
||||
while (true) {
|
||||
stream.next_out = tmpbuf;
|
||||
stream.next_out = tempBuf;
|
||||
stream.next_out_index = 0;
|
||||
stream.avail_out = BUF_SIZE;
|
||||
int status = stream.inflate(JZlib.Z_PARTIAL_FLUSH);
|
||||
final int status = stream.inflate(JZlib.Z_PARTIAL_FLUSH);
|
||||
switch (status) {
|
||||
case JZlib.Z_OK:
|
||||
to.putRawBytes(tmpbuf, 0, BUF_SIZE - stream.avail_out);
|
||||
to.putRawBytes(tempBuf, 0, BUF_SIZE - stream.avail_out);
|
||||
break;
|
||||
case JZlib.Z_BUF_ERROR:
|
||||
return; // wtf.. but this works *head spins*
|
||||
return;
|
||||
default:
|
||||
throw new TransportException(DisconnectReason.COMPRESSION_ERROR, "uncompress: inflate returned "
|
||||
+ status);
|
||||
|
||||
@@ -61,14 +61,17 @@ public class BaseDigest
|
||||
this.bsize = bsize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] digest() {
|
||||
return md.digest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockSize() {
|
||||
return bsize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
md = SecurityUtils.getMessageDigest(algorithm);
|
||||
@@ -77,10 +80,12 @@ public class BaseDigest
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] foo) {
|
||||
update(foo, 0, foo.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] foo, int start, int len) {
|
||||
md.update(foo, start, len);
|
||||
}
|
||||
|
||||
@@ -43,10 +43,12 @@ public class MD5
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Digest> {
|
||||
|
||||
@Override
|
||||
public Digest create() {
|
||||
return new MD5();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "md5";
|
||||
}
|
||||
|
||||
@@ -43,10 +43,12 @@ public class SHA1
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Digest> {
|
||||
|
||||
@Override
|
||||
public Digest create() {
|
||||
return new SHA1();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "sha1";
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ import net.schmizz.sshj.transport.digest.SHA1;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
@@ -72,29 +74,33 @@ public abstract class AbstractDHG
|
||||
private byte[] I_C;
|
||||
|
||||
private byte[] e;
|
||||
private byte[] f;
|
||||
private byte[] K;
|
||||
private byte[] H;
|
||||
private PublicKey hostKey;
|
||||
|
||||
@Override
|
||||
public byte[] getH() {
|
||||
return ByteArrayUtils.copyOf(H);
|
||||
}
|
||||
|
||||
public Digest getHash() {
|
||||
return sha;
|
||||
}
|
||||
|
||||
public PublicKey getHostKey() {
|
||||
return hostKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getK() {
|
||||
return ByteArrayUtils.copyOf(K);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Digest getHash() {
|
||||
return sha;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey getHostKey() {
|
||||
return hostKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Transport trans, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C)
|
||||
throws TransportException {
|
||||
throws GeneralSecurityException, TransportException {
|
||||
this.trans = trans;
|
||||
this.V_S = ByteArrayUtils.copyOf(V_S);
|
||||
this.V_C = ByteArrayUtils.copyOf(V_C);
|
||||
@@ -108,29 +114,30 @@ public abstract class AbstractDHG
|
||||
trans.write(new SSHPacket(Message.KEXDH_INIT).putMPInt(e));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean next(Message msg, SSHPacket packet)
|
||||
throws TransportException {
|
||||
throws GeneralSecurityException, TransportException {
|
||||
if (msg != Message.KEXDH_31)
|
||||
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, "Unxpected packet: " + msg);
|
||||
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, "Unexpected packet: " + msg);
|
||||
|
||||
log.info("Received SSH_MSG_KEXDH_REPLY");
|
||||
final byte[] K_S = packet.readBytes();
|
||||
f = packet.readMPIntAsBytes();
|
||||
final byte[] f = packet.readMPIntAsBytes();
|
||||
final byte[] sig = packet.readBytes(); // signature sent by server
|
||||
dh.setF(f);
|
||||
dh.setF(new BigInteger(f));
|
||||
K = dh.getK();
|
||||
|
||||
hostKey = new Buffer.PlainBuffer(K_S).readPublicKey();
|
||||
|
||||
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer() // our hash
|
||||
.putString(V_C) //
|
||||
.putString(V_S) //
|
||||
.putString(I_C) //
|
||||
.putString(I_S) //
|
||||
.putString(K_S) //
|
||||
.putMPInt(e) //
|
||||
.putMPInt(f) //
|
||||
.putMPInt(K); //
|
||||
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer()
|
||||
.putString(V_C)
|
||||
.putString(V_S)
|
||||
.putString(I_C)
|
||||
.putString(I_S)
|
||||
.putString(K_S)
|
||||
.putMPInt(e)
|
||||
.putMPInt(f)
|
||||
.putMPInt(K);
|
||||
sha.update(buf.array(), 0, buf.available());
|
||||
H = sha.digest();
|
||||
|
||||
@@ -139,7 +146,8 @@ public abstract class AbstractDHG
|
||||
signature.init(hostKey, null);
|
||||
signature.update(H, 0, H.length);
|
||||
if (!signature.verify(sig))
|
||||
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, "KeyExchange signature verification failed");
|
||||
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED,
|
||||
"KeyExchange signature verification failed");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport.kex;
|
||||
|
||||
import net.schmizz.sshj.common.ByteArrayUtils;
|
||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||
import net.schmizz.sshj.common.SecurityUtils;
|
||||
|
||||
@@ -55,79 +54,53 @@ public class DH {
|
||||
private BigInteger p;
|
||||
private BigInteger g;
|
||||
private BigInteger e; // my public key
|
||||
private byte[] e_array;
|
||||
private BigInteger f; // your public key
|
||||
private BigInteger K; // shared secret key
|
||||
private byte[] K_array;
|
||||
private final KeyPairGenerator myKpairGen;
|
||||
private final KeyAgreement myKeyAgree;
|
||||
private final KeyPairGenerator generator;
|
||||
private final KeyAgreement agreement;
|
||||
|
||||
public DH() {
|
||||
try {
|
||||
myKpairGen = SecurityUtils.getKeyPairGenerator("DH");
|
||||
myKeyAgree = SecurityUtils.getKeyAgreement("DH");
|
||||
generator = SecurityUtils.getKeyPairGenerator("DH");
|
||||
agreement = SecurityUtils.getKeyAgreement("DH");
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public byte[] getE() {
|
||||
if (e == null) {
|
||||
DHParameterSpec dhSkipParamSpec = new DHParameterSpec(p, g);
|
||||
KeyPair myKpair;
|
||||
try {
|
||||
myKpairGen.initialize(dhSkipParamSpec);
|
||||
myKpair = myKpairGen.generateKeyPair();
|
||||
myKeyAgree.init(myKpair.getPrivate());
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
e = ((javax.crypto.interfaces.DHPublicKey) myKpair.getPublic()).getY();
|
||||
e_array = e.toByteArray();
|
||||
}
|
||||
return ByteArrayUtils.copyOf(e_array);
|
||||
}
|
||||
|
||||
public byte[] getK() {
|
||||
if (K == null) {
|
||||
try {
|
||||
KeyFactory myKeyFac = SecurityUtils.getKeyFactory("DH");
|
||||
DHPublicKeySpec keySpec = new DHPublicKeySpec(f, p, g);
|
||||
PublicKey yourPubKey = myKeyFac.generatePublic(keySpec);
|
||||
myKeyAgree.doPhase(yourPubKey, true);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
byte[] mySharedSecret = myKeyAgree.generateSecret();
|
||||
K = new BigInteger(mySharedSecret);
|
||||
K_array = mySharedSecret;
|
||||
}
|
||||
return ByteArrayUtils.copyOf(K_array);
|
||||
}
|
||||
|
||||
public void setF(byte[] f) {
|
||||
setF(new BigInteger(f));
|
||||
}
|
||||
|
||||
public void setG(byte[] g) {
|
||||
setG(new BigInteger(g));
|
||||
}
|
||||
|
||||
public void setP(byte[] p) {
|
||||
setP(new BigInteger(p));
|
||||
}
|
||||
|
||||
void setF(BigInteger f) {
|
||||
public void setF(BigInteger f) {
|
||||
this.f = f;
|
||||
}
|
||||
|
||||
void setG(BigInteger g) {
|
||||
public void setG(BigInteger g) {
|
||||
this.g = g;
|
||||
}
|
||||
|
||||
void setP(BigInteger p) {
|
||||
public void setP(BigInteger p) {
|
||||
this.p = p;
|
||||
}
|
||||
|
||||
public byte[] getE()
|
||||
throws GeneralSecurityException {
|
||||
if (e == null) {
|
||||
generator.initialize(new DHParameterSpec(p, g));
|
||||
final KeyPair kp = generator.generateKeyPair();
|
||||
agreement.init(kp.getPrivate());
|
||||
e = ((javax.crypto.interfaces.DHPublicKey) kp.getPublic()).getY();
|
||||
}
|
||||
return e.toByteArray();
|
||||
}
|
||||
|
||||
public byte[] getK()
|
||||
throws GeneralSecurityException {
|
||||
if (K == null) {
|
||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("DH");
|
||||
final DHPublicKeySpec keySpec = new DHPublicKeySpec(f, p, g);
|
||||
final PublicKey yourPubKey = keyFactory.generatePublic(keySpec);
|
||||
agreement.doPhase(yourPubKey, true);
|
||||
K = new BigInteger(agreement.generateSecret());
|
||||
}
|
||||
return K.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,10 +47,12 @@ public class DHG1
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
|
||||
|
||||
@Override
|
||||
public KeyExchange create() {
|
||||
return new DHG1();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "diffie-hellman-group1-sha1";
|
||||
}
|
||||
@@ -59,8 +61,8 @@ public class DHG1
|
||||
|
||||
@Override
|
||||
protected void initDH(DH dh) {
|
||||
dh.setG(DHGroupData.getG());
|
||||
dh.setP(DHGroupData.getP1());
|
||||
dh.setG(DHGroupData.G);
|
||||
dh.setP(DHGroupData.P1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,10 +48,12 @@ public class DHG14
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<KeyExchange> {
|
||||
|
||||
@Override
|
||||
public KeyExchange create() {
|
||||
return new DHG14();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "diffie-hellman-group14-sha1";
|
||||
}
|
||||
@@ -60,8 +62,8 @@ public class DHG14
|
||||
|
||||
@Override
|
||||
protected void initDH(DH dh) {
|
||||
dh.setG(DHGroupData.getG());
|
||||
dh.setP(DHGroupData.getP14());
|
||||
dh.setG(DHGroupData.G);
|
||||
dh.setP(DHGroupData.P14);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,70 +35,28 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport.kex;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/** Simple class holding the data for DH group key exchanges. */
|
||||
public final class DHGroupData {
|
||||
|
||||
public static byte[] getG() {
|
||||
final byte[] G = {2};
|
||||
return G;
|
||||
}
|
||||
public static final BigInteger G =
|
||||
new BigInteger("2");
|
||||
|
||||
public static byte[] getP1() {
|
||||
final byte[] P_1 = {(byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
|
||||
(byte) 0xFF, (byte) 0xFF, (byte) 0xC9, (byte) 0x0F, (byte) 0xDA, (byte) 0xA2, (byte) 0x21, (byte) 0x68,
|
||||
(byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, (byte) 0x62, (byte) 0x8B, (byte) 0x80, (byte) 0xDC,
|
||||
(byte) 0x1C, (byte) 0xD1, (byte) 0x29, (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A, (byte) 0x67,
|
||||
(byte) 0xCC, (byte) 0x74, (byte) 0x02, (byte) 0x0B, (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13,
|
||||
(byte) 0x9B, (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79, (byte) 0x8E, (byte) 0x34,
|
||||
(byte) 0x04, (byte) 0xDD, (byte) 0xEF, (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, (byte) 0x3A,
|
||||
(byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A, (byte) 0x6D, (byte) 0xF2, (byte) 0x5F,
|
||||
(byte) 0x14, (byte) 0x37, (byte) 0x4F, (byte) 0xE1, (byte) 0x35, (byte) 0x6D, (byte) 0x6D, (byte) 0x51,
|
||||
(byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85, (byte) 0xB5, (byte) 0x76, (byte) 0x62, (byte) 0x5E,
|
||||
(byte) 0x7E, (byte) 0xC6, (byte) 0xF4, (byte) 0x4C, (byte) 0x42, (byte) 0xE9, (byte) 0xA6, (byte) 0x37,
|
||||
(byte) 0xED, (byte) 0x6B, (byte) 0x0B, (byte) 0xFF, (byte) 0x5C, (byte) 0xB6, (byte) 0xF4, (byte) 0x06,
|
||||
(byte) 0xB7, (byte) 0xED, (byte) 0xEE, (byte) 0x38, (byte) 0x6B, (byte) 0xFB, (byte) 0x5A, (byte) 0x89,
|
||||
(byte) 0x9F, (byte) 0xA5, (byte) 0xAE, (byte) 0x9F, (byte) 0x24, (byte) 0x11, (byte) 0x7C, (byte) 0x4B,
|
||||
(byte) 0x1F, (byte) 0xE6, (byte) 0x49, (byte) 0x28, (byte) 0x66, (byte) 0x51, (byte) 0xEC, (byte) 0xE6,
|
||||
(byte) 0x53, (byte) 0x81, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
|
||||
(byte) 0xFF, (byte) 0xFF};
|
||||
return P_1;
|
||||
}
|
||||
public static final BigInteger P1 =
|
||||
new BigInteger("1797693134862315907708391567937874531978602960487560117064444236841971802161585193" +
|
||||
"6894783379586492554150218056548598050364644054819923910005079287700335581663922955" +
|
||||
"3136239076508735759914822574862575007425302077447712589550957937778424442426617334" +
|
||||
"727629299387668709205606050270810842907692932019128194467627007");
|
||||
|
||||
public static byte[] getP14() {
|
||||
final byte[] P_14 = {(byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
|
||||
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC9, (byte) 0x0F, (byte) 0xDA, (byte) 0xA2, (byte) 0x21,
|
||||
(byte) 0x68, (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, (byte) 0x62, (byte) 0x8B, (byte) 0x80,
|
||||
(byte) 0xDC, (byte) 0x1C, (byte) 0xD1, (byte) 0x29, (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A,
|
||||
(byte) 0x67, (byte) 0xCC, (byte) 0x74, (byte) 0x02, (byte) 0x0B, (byte) 0xBE, (byte) 0xA6, (byte) 0x3B,
|
||||
(byte) 0x13, (byte) 0x9B, (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79, (byte) 0x8E,
|
||||
(byte) 0x34, (byte) 0x04, (byte) 0xDD, (byte) 0xEF, (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD,
|
||||
(byte) 0x3A, (byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A, (byte) 0x6D, (byte) 0xF2,
|
||||
(byte) 0x5F, (byte) 0x14, (byte) 0x37, (byte) 0x4F, (byte) 0xE1, (byte) 0x35, (byte) 0x6D, (byte) 0x6D,
|
||||
(byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85, (byte) 0xB5, (byte) 0x76, (byte) 0x62,
|
||||
(byte) 0x5E, (byte) 0x7E, (byte) 0xC6, (byte) 0xF4, (byte) 0x4C, (byte) 0x42, (byte) 0xE9, (byte) 0xA6,
|
||||
(byte) 0x37, (byte) 0xED, (byte) 0x6B, (byte) 0x0B, (byte) 0xFF, (byte) 0x5C, (byte) 0xB6, (byte) 0xF4,
|
||||
(byte) 0x06, (byte) 0xB7, (byte) 0xED, (byte) 0xEE, (byte) 0x38, (byte) 0x6B, (byte) 0xFB, (byte) 0x5A,
|
||||
(byte) 0x89, (byte) 0x9F, (byte) 0xA5, (byte) 0xAE, (byte) 0x9F, (byte) 0x24, (byte) 0x11, (byte) 0x7C,
|
||||
(byte) 0x4B, (byte) 0x1F, (byte) 0xE6, (byte) 0x49, (byte) 0x28, (byte) 0x66, (byte) 0x51, (byte) 0xEC,
|
||||
(byte) 0xE4, (byte) 0x5B, (byte) 0x3D, (byte) 0xC2, (byte) 0x00, (byte) 0x7C, (byte) 0xB8, (byte) 0xA1,
|
||||
(byte) 0x63, (byte) 0xBF, (byte) 0x05, (byte) 0x98, (byte) 0xDA, (byte) 0x48, (byte) 0x36, (byte) 0x1C,
|
||||
(byte) 0x55, (byte) 0xD3, (byte) 0x9A, (byte) 0x69, (byte) 0x16, (byte) 0x3F, (byte) 0xA8, (byte) 0xFD,
|
||||
(byte) 0x24, (byte) 0xCF, (byte) 0x5F, (byte) 0x83, (byte) 0x65, (byte) 0x5D, (byte) 0x23, (byte) 0xDC,
|
||||
(byte) 0xA3, (byte) 0xAD, (byte) 0x96, (byte) 0x1C, (byte) 0x62, (byte) 0xF3, (byte) 0x56, (byte) 0x20,
|
||||
(byte) 0x85, (byte) 0x52, (byte) 0xBB, (byte) 0x9E, (byte) 0xD5, (byte) 0x29, (byte) 0x07, (byte) 0x70,
|
||||
(byte) 0x96, (byte) 0x96, (byte) 0x6D, (byte) 0x67, (byte) 0x0C, (byte) 0x35, (byte) 0x4E, (byte) 0x4A,
|
||||
(byte) 0xBC, (byte) 0x98, (byte) 0x04, (byte) 0xF1, (byte) 0x74, (byte) 0x6C, (byte) 0x08, (byte) 0xCA,
|
||||
(byte) 0x18, (byte) 0x21, (byte) 0x7C, (byte) 0x32, (byte) 0x90, (byte) 0x5E, (byte) 0x46, (byte) 0x2E,
|
||||
(byte) 0x36, (byte) 0xCE, (byte) 0x3B, (byte) 0xE3, (byte) 0x9E, (byte) 0x77, (byte) 0x2C, (byte) 0x18,
|
||||
(byte) 0x0E, (byte) 0x86, (byte) 0x03, (byte) 0x9B, (byte) 0x27, (byte) 0x83, (byte) 0xA2, (byte) 0xEC,
|
||||
(byte) 0x07, (byte) 0xA2, (byte) 0x8F, (byte) 0xB5, (byte) 0xC5, (byte) 0x5D, (byte) 0xF0, (byte) 0x6F,
|
||||
(byte) 0x4C, (byte) 0x52, (byte) 0xC9, (byte) 0xDE, (byte) 0x2B, (byte) 0xCB, (byte) 0xF6, (byte) 0x95,
|
||||
(byte) 0x58, (byte) 0x17, (byte) 0x18, (byte) 0x39, (byte) 0x95, (byte) 0x49, (byte) 0x7C, (byte) 0xEA,
|
||||
(byte) 0x95, (byte) 0x6A, (byte) 0xE5, (byte) 0x15, (byte) 0xD2, (byte) 0x26, (byte) 0x18, (byte) 0x98,
|
||||
(byte) 0xFA, (byte) 0x05, (byte) 0x10, (byte) 0x15, (byte) 0x72, (byte) 0x8E, (byte) 0x5A, (byte) 0x8A,
|
||||
(byte) 0xAC, (byte) 0xAA, (byte) 0x68, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
|
||||
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
|
||||
return P_14;
|
||||
}
|
||||
public static final BigInteger P14 =
|
||||
new BigInteger("3231700607131100730033891392642382824881794124114023911284200975140074170663435422" +
|
||||
"2619689417363569347117901737909704191754605873209195028853758986185622153212175412" +
|
||||
"5149017745202702357960782362488842461894775876411059286460994117232454266225221932" +
|
||||
"3054091903768052423551912567971587011700105805587765103886184728025797605490356973" +
|
||||
"2561526167081339361799541336476559160368317896729073178384589680639671900977202194" +
|
||||
"1686472258710314113364293195361934716365332097170774482279885885653692086452966360" +
|
||||
"7725026895550592836275112117409697299806841055435958486658329164213621823107899099" +
|
||||
"9448652468262416972035911852507045361090559");
|
||||
|
||||
}
|
||||
|
||||
@@ -41,14 +41,33 @@ import net.schmizz.sshj.transport.Transport;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
import net.schmizz.sshj.transport.digest.Digest;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/** Key exchange algorithm. */
|
||||
public interface KeyExchange {
|
||||
|
||||
/**
|
||||
* Initialize the key exchange algorithm.
|
||||
*
|
||||
* @param trans the transport
|
||||
* @param V_S the server identification string
|
||||
* @param V_C the client identification string
|
||||
* @param I_S the server key init packet
|
||||
* @param I_C the client key init packet
|
||||
*
|
||||
* @throws GeneralSecurityException
|
||||
* @throws TransportException if there is an error sending a packet
|
||||
*/
|
||||
void init(Transport trans, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C)
|
||||
throws GeneralSecurityException, TransportException;
|
||||
|
||||
/** @return the computed H parameter */
|
||||
byte[] getH();
|
||||
|
||||
/** @return the computed K parameter */
|
||||
byte[] getK();
|
||||
|
||||
/**
|
||||
* The message digest used by this key exchange algorithm.
|
||||
*
|
||||
@@ -59,23 +78,6 @@ public interface KeyExchange {
|
||||
/** @return the host key determined from server's response packets */
|
||||
PublicKey getHostKey();
|
||||
|
||||
/** @return the computed K parameter */
|
||||
byte[] getK();
|
||||
|
||||
/**
|
||||
* Initialize the key exchange algorithm.
|
||||
*
|
||||
* @param trans the transport
|
||||
* @param V_S the server identification string
|
||||
* @param V_C the client identification string
|
||||
* @param I_S the server key init packet
|
||||
* @param I_C the client key init packet
|
||||
*
|
||||
* @throws TransportException if there is an error sending a packet
|
||||
*/
|
||||
void init(Transport trans, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C)
|
||||
throws TransportException;
|
||||
|
||||
/**
|
||||
* Process the next packet
|
||||
*
|
||||
@@ -84,9 +86,10 @@ public interface KeyExchange {
|
||||
*
|
||||
* @return a boolean indicating if the processing is complete or if more packets are to be received
|
||||
*
|
||||
* @throws TransportException if there is an error sending a packet
|
||||
* @throws GeneralSecurityException
|
||||
* @throws TransportException if there is an error sending a packet
|
||||
*/
|
||||
boolean next(Message msg, SSHPacket buffer)
|
||||
throws TransportException;
|
||||
throws GeneralSecurityException, TransportException;
|
||||
|
||||
}
|
||||
|
||||
@@ -59,14 +59,17 @@ public class BaseMAC
|
||||
tmp = new byte[defbsize];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] doFinal() {
|
||||
return mac.doFinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] doFinal(byte[] input) {
|
||||
return mac.doFinal(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFinal(byte[] buf, int offset) {
|
||||
try {
|
||||
if (bsize != defbsize) {
|
||||
@@ -79,10 +82,12 @@ public class BaseMAC
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockSize() {
|
||||
return bsize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(byte[] key) {
|
||||
if (key.length > defbsize) {
|
||||
byte[] tmp = new byte[defbsize];
|
||||
@@ -99,14 +104,17 @@ public class BaseMAC
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte foo[], int s, int l) {
|
||||
mac.update(foo, s, l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] foo) {
|
||||
mac.update(foo, 0, foo.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(long i) {
|
||||
tmp[0] = (byte) (i >>> 24);
|
||||
tmp[1] = (byte) (i >>> 16);
|
||||
|
||||
@@ -43,10 +43,12 @@ public class HMACMD5
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<MAC> {
|
||||
|
||||
@Override
|
||||
public MAC create() {
|
||||
return new HMACMD5();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "hmac-md5";
|
||||
}
|
||||
|
||||
@@ -43,10 +43,12 @@ public class HMACMD596
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<MAC> {
|
||||
|
||||
@Override
|
||||
public MAC create() {
|
||||
return new HMACMD596();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "hmac-md5-96";
|
||||
}
|
||||
|
||||
@@ -43,10 +43,12 @@ public class HMACSHA1
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<MAC> {
|
||||
|
||||
@Override
|
||||
public MAC create() {
|
||||
return new HMACSHA1();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "hmac-sha1";
|
||||
}
|
||||
|
||||
@@ -43,10 +43,12 @@ public class HMACSHA196
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<MAC> {
|
||||
|
||||
@Override
|
||||
public MAC create() {
|
||||
return new HMACSHA196();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "hmac-sha1-96";
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ public class BouncyCastleRandom
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory<Random> {
|
||||
|
||||
@Override
|
||||
public Random create() {
|
||||
return new BouncyCastleRandom();
|
||||
}
|
||||
@@ -65,6 +66,7 @@ public class BouncyCastleRandom
|
||||
random.addSeedMaterial(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(byte[] bytes, int start, int len) {
|
||||
random.nextBytes(bytes, start, len);
|
||||
}
|
||||
|
||||
@@ -45,10 +45,12 @@ public class JCERandom
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Random> {
|
||||
|
||||
@Override
|
||||
public Random create() {
|
||||
return new JCERandom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "default";
|
||||
}
|
||||
@@ -69,6 +71,7 @@ public class JCERandom
|
||||
* @param start the offset to start at
|
||||
* @param len the number of bytes to fill
|
||||
*/
|
||||
@Override
|
||||
public synchronized void fill(byte[] foo, int start, int len) {
|
||||
if (start == 0 && len == foo.length)
|
||||
random.nextBytes(foo);
|
||||
|
||||
@@ -46,10 +46,12 @@ public class SingletonRandomFactory
|
||||
random = factory.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Random create() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(byte[] bytes, int start, int len) {
|
||||
random.fill(bytes, start, len);
|
||||
}
|
||||
|
||||
@@ -60,8 +60,7 @@ public class ConsoleKnownHostsVerifier
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hostKeyChangedAction(Entry entry, String hostname, PublicKey key)
|
||||
throws IOException {
|
||||
protected boolean hostKeyChangedAction(Entry entry, String hostname, PublicKey key) {
|
||||
final KeyType type = KeyType.fromKey(key);
|
||||
final String fp = SecurityUtils.getFingerprint(key);
|
||||
final String path = getFile().getAbsolutePath();
|
||||
|
||||
@@ -131,6 +131,7 @@ public class OpenSSHKnownHosts
|
||||
init(parts[1], parts[2]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appliesTo(String host) {
|
||||
for (String h : hosts)
|
||||
if (host.equals(h))
|
||||
@@ -138,6 +139,7 @@ public class OpenSSHKnownHosts
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHostPart() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (String host : hosts) {
|
||||
@@ -185,6 +187,7 @@ public class OpenSSHKnownHosts
|
||||
init(parts[1], parts[2]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appliesTo(String host)
|
||||
throws IOException {
|
||||
return hashedHost.equals(hashHost(host));
|
||||
@@ -204,14 +207,14 @@ public class OpenSSHKnownHosts
|
||||
return saltyBytes;
|
||||
}
|
||||
|
||||
private String getSalt()
|
||||
throws IOException {
|
||||
private String getSalt() {
|
||||
if (salt == null) {
|
||||
salt = Base64.encodeBytes(saltyBytes);
|
||||
}
|
||||
return salt;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHostPart() {
|
||||
return hashedHost;
|
||||
}
|
||||
@@ -247,6 +250,7 @@ public class OpenSSHKnownHosts
|
||||
return khFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(final String hostname, final int port, final PublicKey key) {
|
||||
final KeyType type = KeyType.fromKey(key);
|
||||
if (type == KeyType.UNKNOWN)
|
||||
@@ -269,8 +273,7 @@ public class OpenSSHKnownHosts
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean hostKeyChangedAction(Entry entry, String hostname, PublicKey key)
|
||||
throws IOException {
|
||||
protected boolean hostKeyChangedAction(Entry entry, String hostname, PublicKey key) {
|
||||
log.warn("Host key for `{}` has changed!", hostname);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.security.PublicKey;
|
||||
public final class PromiscuousVerifier
|
||||
implements HostKeyVerifier {
|
||||
|
||||
@Override
|
||||
public boolean verify(String hostname, int port, PublicKey key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ public class UserAuthException
|
||||
|
||||
public static final ExceptionChainer<UserAuthException> chainer = new ExceptionChainer<UserAuthException>() {
|
||||
|
||||
@Override
|
||||
public UserAuthException chain(Throwable t) {
|
||||
if (t instanceof UserAuthException)
|
||||
return (UserAuthException) t;
|
||||
|
||||
@@ -59,6 +59,7 @@ public class UserAuthImpl
|
||||
|
||||
// synchronized for mutual exclusion; ensure one authenticate() ever in progress
|
||||
|
||||
@Override
|
||||
public synchronized void authenticate(String username, Service nextService, Iterable<AuthMethod> methods)
|
||||
throws UserAuthException, TransportException {
|
||||
clearState();
|
||||
@@ -108,14 +109,17 @@ public class UserAuthImpl
|
||||
throw new UserAuthException("Exhausted available authentication methods", savedEx.peek());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBanner() {
|
||||
return banner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNextServiceName() {
|
||||
return nextService.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transport getTransport() {
|
||||
return trans;
|
||||
}
|
||||
@@ -126,14 +130,17 @@ public class UserAuthImpl
|
||||
*
|
||||
* @return deque of saved exceptions
|
||||
*/
|
||||
@Override
|
||||
public Deque<UserAuthException> getSavedExceptions() {
|
||||
return savedEx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hadPartialSuccess() {
|
||||
return partialSuccess;
|
||||
}
|
||||
|
||||
@@ -12,26 +12,6 @@
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file may incorporate work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package net.schmizz.sshj.userauth.keyprovider;
|
||||
|
||||
|
||||
@@ -12,26 +12,6 @@
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file may incorporate work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package net.schmizz.sshj.userauth.keyprovider;
|
||||
|
||||
@@ -57,14 +37,17 @@ public class KeyPairWrapper
|
||||
this(new KeyPair(publicKey, privateKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivate() {
|
||||
return kp.getPrivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey getPublic() {
|
||||
return kp.getPublic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -38,10 +38,12 @@ public class OpenSSHKeyFile
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
|
||||
|
||||
@Override
|
||||
public FileKeyProvider create() {
|
||||
return new OpenSSHKeyFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "OpenSSH";
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.userauth.password.PasswordFinder;
|
||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||
import net.schmizz.sshj.userauth.password.PrivateKeyFileResource;
|
||||
import net.schmizz.sshj.userauth.password.Resource;
|
||||
import org.bouncycastle.openssl.EncryptionException;
|
||||
import org.bouncycastle.openssl.PEMReader;
|
||||
import org.slf4j.Logger;
|
||||
@@ -40,10 +39,12 @@ public class PKCS8KeyFile
|
||||
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<FileKeyProvider> {
|
||||
@Override
|
||||
public FileKeyProvider create() {
|
||||
return new PKCS8KeyFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "PKCS8";
|
||||
}
|
||||
@@ -51,35 +52,38 @@ public class PKCS8KeyFile
|
||||
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
protected PasswordFinder pwdf;
|
||||
protected File location;
|
||||
protected Resource resource;
|
||||
protected PrivateKeyFileResource resource;
|
||||
protected KeyPair kp;
|
||||
|
||||
protected KeyType type;
|
||||
|
||||
protected char[] passphrase; // for blanking out
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivate()
|
||||
throws IOException {
|
||||
return kp != null ? kp.getPrivate() : (kp = readKeyPair()).getPrivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey getPublic()
|
||||
throws IOException {
|
||||
return kp != null ? kp.getPublic() : (kp = readKeyPair()).getPublic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyType getType()
|
||||
throws IOException {
|
||||
return type != null ? type : (type = KeyType.fromKey(getPublic()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(File location) {
|
||||
assert location != null;
|
||||
this.location = location;
|
||||
resource = new PrivateKeyFileResource(location.getAbsolutePath());
|
||||
resource = new PrivateKeyFileResource(location.getAbsoluteFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(File location, PasswordFinder pwdf) {
|
||||
init(location);
|
||||
this.pwdf = pwdf;
|
||||
@@ -90,6 +94,7 @@ public class PKCS8KeyFile
|
||||
return null;
|
||||
else
|
||||
return new org.bouncycastle.openssl.PasswordFinder() {
|
||||
@Override
|
||||
public char[] getPassword() {
|
||||
return passphrase = pwdf.reqPassword(resource);
|
||||
}
|
||||
@@ -106,7 +111,7 @@ public class PKCS8KeyFile
|
||||
for (; ;) {
|
||||
// while the PasswordFinder tells us we should retry
|
||||
try {
|
||||
r = new PEMReader(new InputStreamReader(new FileInputStream(location)), pFinder);
|
||||
r = new PEMReader(new InputStreamReader(new FileInputStream(resource.getDetail())), pFinder);
|
||||
o = r.readObject();
|
||||
} catch (EncryptionException e) {
|
||||
if (pwdf.shouldRetry(resource))
|
||||
@@ -123,7 +128,7 @@ public class PKCS8KeyFile
|
||||
}
|
||||
|
||||
if (o == null)
|
||||
throw new IOException("Could not read key pair from: " + location);
|
||||
throw new IOException("Could not read key pair from: " + resource);
|
||||
if (o instanceof KeyPair)
|
||||
kp = (KeyPair) o;
|
||||
else
|
||||
@@ -131,4 +136,8 @@ public class PKCS8KeyFile
|
||||
return kp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PKCS8KeyFile{resource=" + resource + "}";
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user