mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 15:20:54 +03:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20c5ab8dfc | ||
|
|
d9c438ed16 | ||
|
|
653e8ad4f2 | ||
|
|
c46dc913e8 | ||
|
|
069ebbd47d | ||
|
|
da2cec8fa2 | ||
|
|
75caa8bcf3 | ||
|
|
f664b7b24f | ||
|
|
70f3aeee68 | ||
|
|
882d40a1b6 | ||
|
|
9649b2f72e | ||
|
|
79a8d0b3ad | ||
|
|
2e7fcfd308 | ||
|
|
946422112d | ||
|
|
b11f0be894 | ||
|
|
ba6e5292c8 | ||
|
|
c8de9ed915 | ||
|
|
7ccd078e52 | ||
|
|
0aa8d5e141 | ||
|
|
2e32bb9aca | ||
|
|
2f4fa62b14 | ||
|
|
8a4367cc7a | ||
|
|
168272ad3b | ||
|
|
17eb5cff0f | ||
|
|
ebd5036d64 | ||
|
|
7797d774ac | ||
|
|
888a8f60d7 | ||
|
|
974e88efb4 | ||
|
|
9a4a24737f | ||
|
|
a1d17982ae | ||
|
|
3beee8350d | ||
|
|
3cd446b462 | ||
|
|
486dbf2b05 | ||
|
|
3cb235bbfd | ||
|
|
2882129211 | ||
|
|
fb97ccb67c | ||
|
|
8b21eff1d2 | ||
|
|
7874e7dbfd | ||
|
|
efc7702195 | ||
|
|
34a7b8e065 | ||
|
|
50c42b97a3 | ||
|
|
826660ab3f | ||
|
|
a3b6fde44a | ||
|
|
69555e9c74 | ||
|
|
241f61bdd1 | ||
|
|
0051dd420c | ||
|
|
f2abc4b397 | ||
|
|
fe0d42fc97 | ||
|
|
19e4670c24 | ||
|
|
fbd6e00720 | ||
|
|
f69cdb1505 | ||
|
|
135b1c819b | ||
|
|
9c51b862cd | ||
|
|
a6353cbb2d | ||
|
|
f11055a726 | ||
|
|
da98153ab6 |
14
README.rst
14
README.rst
@@ -1,3 +1,8 @@
|
||||
.. image:: http://api.flattr.com/button/button-compact-static-100x17.png
|
||||
:align: right
|
||||
:alt: Flattr this
|
||||
:target: http://flattr.com/thing/49085/sshj-ssh-scp-and-sftp-library-for-java
|
||||
|
||||
sshj - SSHv2 library for Java
|
||||
==============================
|
||||
|
||||
@@ -6,10 +11,10 @@ To get started, have a look at one of the examples. Hopefully you will find the
|
||||
Features of the library include:
|
||||
|
||||
* reading known_hosts files for host key verification
|
||||
* password and publickey authentication
|
||||
* publickey, password and keyboard-interactive authentication
|
||||
* command, subsystem and shell channels
|
||||
* local and remote port forwarding
|
||||
* scp + complete sftp version 3 implementation
|
||||
* scp + complete sftp version 0-3 implementation
|
||||
|
||||
Implementations of the following algorithms are included:
|
||||
|
||||
@@ -37,8 +42,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.
|
||||
jzlib_ is required for using zlib compression.
|
||||
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.
|
||||
|
||||
|
||||
Help and discussion
|
||||
@@ -53,8 +57,6 @@ Contributing
|
||||
Fork away!
|
||||
|
||||
|
||||
.. _buildr: http://buildr.apache.org/installing.html
|
||||
|
||||
.. _slf4j: http://www.slf4j.org/download.html
|
||||
|
||||
.. _bouncycastle: http://www.bouncycastle.org/java.html
|
||||
|
||||
18
pom.xml
18
pom.xml
@@ -6,7 +6,7 @@
|
||||
<groupId>net.schmizz</groupId>
|
||||
<artifactId>sshj</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.2.0</version>
|
||||
<version>0.3.1</version>
|
||||
|
||||
<name>sshj</name>
|
||||
<description>SSHv2 library for Java</description>
|
||||
@@ -37,11 +37,11 @@
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.6.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15</artifactId>
|
||||
<artifactId>bcprov-jdk16</artifactId>
|
||||
<version>1.45</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
@@ -66,13 +66,13 @@
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>0.9.20</version>
|
||||
<version>0.9.24</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>0.9.20</version>
|
||||
<version>0.9.24</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -111,7 +111,7 @@
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.2-beta-4</version>
|
||||
<version>2.2-beta-5</version>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/assemble/examples.xml</descriptor>
|
||||
@@ -176,7 +176,7 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15</artifactId>
|
||||
<artifactId>bcprov-jdk16</artifactId>
|
||||
<version>1.45</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -187,12 +187,12 @@
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>0.9.20</version>
|
||||
<version>0.9.24</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>0.9.20</version>
|
||||
<version>0.9.24</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
@@ -16,27 +16,32 @@
|
||||
package examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session.Command;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** This examples demonstrates how a remote command can be executed. */
|
||||
public class Exec {
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
final SSHClient ssh = new SSHClient();
|
||||
ssh.loadKnownHosts();
|
||||
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
|
||||
Command cmd = ssh.startSession().exec("ping -c 1 google.com");
|
||||
|
||||
System.out.print(cmd.getOutputAsString());
|
||||
System.out.println("\n** exit status: " + cmd.getExitStatus());
|
||||
final Session session = ssh.startSession();
|
||||
try {
|
||||
final Command cmd = session.exec("ping -c 1 google.com");
|
||||
System.out.print(cmd.getOutputAsString());
|
||||
cmd.join(5, TimeUnit.SECONDS);
|
||||
System.out.println("\n** exit status: " + cmd.getExitStatus());
|
||||
} finally {
|
||||
session.close();
|
||||
}
|
||||
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
|
||||
@@ -37,35 +37,35 @@ class RudimentaryPTY {
|
||||
ssh.addHostKeyVerifier(new ConsoleKnownHostsVerifier(khFile, System.console()));
|
||||
|
||||
ssh.connect("localhost");
|
||||
|
||||
Shell shell = null;
|
||||
|
||||
try {
|
||||
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
|
||||
final Session session = ssh.startSession();
|
||||
session.allocateDefaultPTY();
|
||||
try {
|
||||
|
||||
shell = session.startShell();
|
||||
session.allocateDefaultPTY();
|
||||
|
||||
new StreamCopier("stdout", shell.getInputStream(), System.out)
|
||||
.bufSize(shell.getLocalMaxPacketSize())
|
||||
.start();
|
||||
final Shell shell = session.startShell();
|
||||
|
||||
new StreamCopier("stderr", shell.getErrorStream(), System.err)
|
||||
.bufSize(shell.getLocalMaxPacketSize())
|
||||
.start();
|
||||
new StreamCopier("stdout", shell.getInputStream(), System.out)
|
||||
.bufSize(shell.getLocalMaxPacketSize())
|
||||
.start();
|
||||
|
||||
// Now make System.in act as stdin. To exit, hit Ctrl+D (since that results in an EOF on System.in)
|
||||
// This is kinda messy because java only allows console input after you hit return
|
||||
// But this is just an example... a GUI app could implement a proper PTY
|
||||
StreamCopier.copy(System.in, shell.getOutputStream(), shell.getRemoteMaxPacketSize(), true);
|
||||
new StreamCopier("stderr", shell.getErrorStream(), System.err)
|
||||
.bufSize(shell.getLocalMaxPacketSize())
|
||||
.start();
|
||||
|
||||
// Now make System.in act as stdin. To exit, hit Ctrl+D (since that results in an EOF on System.in)
|
||||
// This is kinda messy because java only allows console input after you hit return
|
||||
// But this is just an example... a GUI app could implement a proper PTY
|
||||
StreamCopier.copy(System.in, shell.getOutputStream(), shell.getRemoteMaxPacketSize(), true);
|
||||
|
||||
} finally {
|
||||
session.close();
|
||||
}
|
||||
|
||||
} finally {
|
||||
|
||||
if (shell != null)
|
||||
shell.close();
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.sftp.SFTPClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -24,14 +25,19 @@ public class SFTPDownload {
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
final SSHClient ssh = new SSHClient();
|
||||
ssh.loadKnownHosts();
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
final String src = "test_file";
|
||||
final String target = "/tmp/";
|
||||
ssh.newSFTPClient().get(src, target);
|
||||
final SFTPClient sftp = ssh.newSFTPClient();
|
||||
try {
|
||||
sftp.get(src, target);
|
||||
} finally {
|
||||
sftp.close();
|
||||
}
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.sftp.SFTPClient;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -25,14 +26,19 @@ public class SFTPUpload {
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
final SSHClient ssh = new SSHClient();
|
||||
ssh.loadKnownHosts();
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
final String src = System.getProperty("user.home") + File.separator + "test_file";
|
||||
final String target = "/tmp/";
|
||||
ssh.newSFTPClient().put(src, target);
|
||||
final SFTPClient sftp = ssh.newSFTPClient();
|
||||
try {
|
||||
sftp.put(src, target);
|
||||
} finally {
|
||||
sftp.close();
|
||||
}
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class X11 {
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException, InterruptedException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
final SSHClient ssh = new SSHClient();
|
||||
|
||||
// Compression makes X11 more feasible over slower connections
|
||||
// ssh.useCompression();
|
||||
@@ -55,7 +55,7 @@ public class X11 {
|
||||
*/
|
||||
sess.reqX11Forwarding("MIT-MAGIC-COOKIE-1", "b0956167c9ad8f34c8a2788878307dc9", 0);
|
||||
|
||||
Command cmd = sess.exec("/usr/X11/bin/xcalc");
|
||||
final Command cmd = sess.exec("/usr/X11/bin/xcalc");
|
||||
|
||||
new StreamCopier("stdout", cmd.getInputStream(), System.out).start();
|
||||
new StreamCopier("stderr", cmd.getErrorStream(), System.err).start();
|
||||
|
||||
@@ -32,8 +32,9 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
*/
|
||||
public class Future<V, T extends Throwable> {
|
||||
|
||||
private final Logger log;
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final String name;
|
||||
private final ExceptionChainer<T> chainer;
|
||||
private final ReentrantLock lock;
|
||||
private final Condition cond;
|
||||
@@ -60,7 +61,7 @@ public class Future<V, T extends Throwable> {
|
||||
* @param lock lock to use
|
||||
*/
|
||||
public Future(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
|
||||
this.log = LoggerFactory.getLogger("<< " + name + " >>");
|
||||
this.name = name;
|
||||
this.chainer = chainer;
|
||||
this.lock = lock == null ? new ReentrantLock() : lock;
|
||||
this.cond = this.lock.newCondition();
|
||||
@@ -74,7 +75,7 @@ public class Future<V, T extends Throwable> {
|
||||
public void set(V val) {
|
||||
lock();
|
||||
try {
|
||||
log.debug("Setting to `{}`", val);
|
||||
log.debug("Setting <<{}>> to `{}`", name, val);
|
||||
this.val = val;
|
||||
cond.signalAll();
|
||||
} finally {
|
||||
@@ -139,14 +140,14 @@ public class Future<V, T extends Throwable> {
|
||||
throw pendingEx;
|
||||
if (val != null)
|
||||
return val;
|
||||
log.debug("Awaiting");
|
||||
log.debug("Awaiting <<{}>>", name);
|
||||
while (val == null && pendingEx == null)
|
||||
if (timeout == 0)
|
||||
cond.await();
|
||||
else if (!cond.await(timeout, unit))
|
||||
throw chainer.chain(new TimeoutException("Timeout expired"));
|
||||
if (pendingEx != null) {
|
||||
log.error("Woke to: {}", pendingEx.toString());
|
||||
log.error("<<{}>> woke to: {}", name, pendingEx.toString());
|
||||
throw pendingEx;
|
||||
}
|
||||
return val;
|
||||
|
||||
28
src/main/java/net/schmizz/sshj/AndroidConfig.java
Normal file
28
src/main/java/net/schmizz/sshj/AndroidConfig.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
*
|
||||
* Licensed 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;
|
||||
|
||||
import net.schmizz.sshj.transport.random.JCERandom;
|
||||
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
|
||||
|
||||
public class AndroidConfig extends DefaultConfig {
|
||||
|
||||
@Override
|
||||
protected void initRandomFactory(boolean ignored) {
|
||||
setRandomFactory(new SingletonRandomFactory(new JCERandom.Factory()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -91,7 +91,7 @@ public class DefaultConfig
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private static final String VERSION = "SSHJ_0_1";
|
||||
private static final String VERSION = "SSHJ_0_3";
|
||||
|
||||
public DefaultConfig() {
|
||||
setVersion(VERSION);
|
||||
@@ -134,6 +134,7 @@ public class DefaultConfig
|
||||
new TripleDESCBC.Factory(),
|
||||
new BlowfishCBC.Factory()));
|
||||
|
||||
boolean warn = false;
|
||||
// Ref. https://issues.apache.org/jira/browse/SSHD-24
|
||||
// "AES256 and AES192 requires unlimited cryptography extension"
|
||||
for (Iterator<Factory.Named<Cipher>> i = avail.iterator(); i.hasNext();) {
|
||||
@@ -144,10 +145,12 @@ public class DefaultConfig
|
||||
final byte[] iv = new byte[c.getIVSize()];
|
||||
c.init(Cipher.Mode.Encrypt, key, iv);
|
||||
} catch (Exception e) {
|
||||
log.warn("Disabling cipher `{}`: cipher strengths apparently limited by JCE policy", f.getName());
|
||||
warn = true;
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
if (warn)
|
||||
log.warn("Disabling high-strength ciphers: cipher strengths apparently limited by JCE policy");
|
||||
|
||||
setCipherFactories(avail);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder.Forward
|
||||
import net.schmizz.sshj.connection.channel.forwarded.X11Forwarder;
|
||||
import net.schmizz.sshj.connection.channel.forwarded.X11Forwarder.X11Channel;
|
||||
import net.schmizz.sshj.sftp.SFTPClient;
|
||||
import net.schmizz.sshj.sftp.SFTPEngine;
|
||||
import net.schmizz.sshj.sftp.StatefulSFTPClient;
|
||||
import net.schmizz.sshj.transport.Transport;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
@@ -94,13 +95,15 @@ import java.util.List;
|
||||
* client = new SSHClient();
|
||||
* client.initUserKnownHosts();
|
||||
* client.connect("hostname");
|
||||
* try
|
||||
* {
|
||||
* try {
|
||||
* client.authPassword("username", "password");
|
||||
* client.startSession().exec("true");
|
||||
* client.getConnection().join();
|
||||
* } finally
|
||||
* {
|
||||
* final Session session = client.startSession();
|
||||
* try {
|
||||
* session.exec("true");
|
||||
* client.getConnection().join();
|
||||
* } finally {
|
||||
* session.close();
|
||||
* } finally {
|
||||
* client.disconnect();
|
||||
* }
|
||||
* </pre>
|
||||
@@ -118,7 +121,6 @@ public class SSHClient
|
||||
/** Logger */
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
|
||||
/** Transport layer */
|
||||
protected final Transport trans;
|
||||
|
||||
@@ -156,20 +158,18 @@ public class SSHClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link HostKeyVerifier} that will verify any host at given {@code hostname:port} and a host key that has
|
||||
* the given {@code fingerprint}, e.g. {@code "4b:69:6c:72:6f:79:20:77:61:73:20:68:65:72:65:21"}
|
||||
* Add a {@link HostKeyVerifier} that will verify any host that's able to claim a host key with the given {@code
|
||||
* fingerprint}, e.g. {@code "4b:69:6c:72:6f:79:20:77:61:73:20:68:65:72:65:21"}
|
||||
*
|
||||
* @param host the hostname / IP address
|
||||
* @param port the port for which the {@code fingerprint} applies
|
||||
* @param fingerprint expected fingerprint in colon-delimited format (16 octets in hex delimited by a colon)
|
||||
*
|
||||
* @see SecurityUtils#getFingerprint
|
||||
*/
|
||||
public void addHostKeyVerifier(final String host, final int port, final String fingerprint) {
|
||||
public void addHostKeyVerifier(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);
|
||||
return SecurityUtils.getFingerprint(k).equals(fingerprint);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -595,7 +595,7 @@ public class SSHClient
|
||||
public SFTPClient newSFTPClient()
|
||||
throws IOException {
|
||||
assert isConnected() && isAuthenticated();
|
||||
return new SFTPClient(this);
|
||||
return new SFTPClient(new SFTPEngine(this).init());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,39 +18,32 @@ package net.schmizz.sshj.common;
|
||||
/** Disconnect error codes */
|
||||
public enum DisconnectReason {
|
||||
|
||||
UNKNOWN(0),
|
||||
HOST_NOT_ALLOWED_TO_CONNECT(1),
|
||||
PROTOCOL_ERROR(2),
|
||||
KEY_EXCHANGE_FAILED(3),
|
||||
HOST_AUTHENTICATION_FAILED(4),
|
||||
RESERVED(4),
|
||||
MAC_ERROR(5),
|
||||
COMPRESSION_ERROR(6),
|
||||
SERVICE_NOT_AVAILABLE(7),
|
||||
PROTOCOL_VERSION_NOT_SUPPORTED(8),
|
||||
HOST_KEY_NOT_VERIFIABLE(9),
|
||||
CONNECTION_LOST(10),
|
||||
BY_APPLICATION(11),
|
||||
TOO_MANY_CONNECTIONS(12),
|
||||
AUTH_CANCELLED_BY_USER(13),
|
||||
NO_MORE_AUTH_METHODS_AVAILABLE(14),
|
||||
ILLEGAL_USER_NAME(15);
|
||||
UNKNOWN,
|
||||
HOST_NOT_ALLOWED_TO_CONNECT,
|
||||
PROTOCOL_ERROR,
|
||||
KEY_EXCHANGE_FAILED,
|
||||
RESERVED,
|
||||
MAC_ERROR,
|
||||
COMPRESSION_ERROR,
|
||||
SERVICE_NOT_AVAILABLE,
|
||||
PROTOCOL_VERSION_NOT_SUPPORTED,
|
||||
HOST_KEY_NOT_VERIFIABLE,
|
||||
CONNECTION_LOST,
|
||||
BY_APPLICATION,
|
||||
TOO_MANY_CONNECTIONS,
|
||||
AUTH_CANCELLED_BY_USER,
|
||||
NO_MORE_AUTH_METHODS_AVAILABLE,
|
||||
ILLEGAL_USER_NAME;
|
||||
|
||||
public static DisconnectReason fromInt(int code) {
|
||||
for (DisconnectReason dc : values())
|
||||
if (dc.code == code)
|
||||
return dc;
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
private final int code;
|
||||
|
||||
private DisconnectReason(int code) {
|
||||
this.code = code;
|
||||
final int len = values().length;
|
||||
if (code < 0 || code > len)
|
||||
return UNKNOWN;
|
||||
return values()[code];
|
||||
}
|
||||
|
||||
public int toInt() {
|
||||
return code;
|
||||
return ordinal();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,16 +40,16 @@ public enum KeyType {
|
||||
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);
|
||||
@@ -70,6 +70,7 @@ public enum KeyType {
|
||||
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;
|
||||
@@ -120,7 +121,6 @@ public enum KeyType {
|
||||
|
||||
public abstract void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
|
||||
|
||||
|
||||
protected abstract boolean isMyType(Key key);
|
||||
|
||||
public static KeyType fromKey(Key key) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package net.schmizz.sshj.common;
|
||||
/** SSH message identifiers */
|
||||
public enum Message {
|
||||
|
||||
UNKNOWN(0),
|
||||
DISCONNECT(1),
|
||||
IGNORE(2),
|
||||
UNIMPLEMENTED(3),
|
||||
@@ -67,8 +68,11 @@ public enum Message {
|
||||
|
||||
static {
|
||||
for (Message c : Message.values())
|
||||
if (cache[c.toByte()] == null)
|
||||
cache[c.toByte()] = c;
|
||||
cache[c.toByte()] = c;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (cache[i] == null)
|
||||
cache[i] = UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public static Message fromByte(byte b) {
|
||||
|
||||
@@ -76,11 +76,7 @@ public class SSHPacket
|
||||
* @return the message identifier
|
||||
*/
|
||||
public Message readMessageID() {
|
||||
byte b = readByte();
|
||||
Message cmd = Message.fromByte(b);
|
||||
if (cmd == null)
|
||||
throw new BufferException("Unknown message ID: " + b);
|
||||
return cmd;
|
||||
return Message.fromByte(readByte());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -86,7 +86,7 @@ public class StreamCopier
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private final Logger log;
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final InputStream in;
|
||||
private final OutputStream out;
|
||||
@@ -103,9 +103,7 @@ public class StreamCopier
|
||||
public StreamCopier(String name, InputStream in, OutputStream out) {
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
|
||||
setName("streamCopier");
|
||||
log = LoggerFactory.getLogger(name);
|
||||
setName(name);
|
||||
}
|
||||
|
||||
public StreamCopier bufSize(int size) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener;
|
||||
import net.schmizz.sshj.transport.Transport;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
@@ -248,9 +249,9 @@ public class ConnectionImpl
|
||||
public void notifyDisconnect()
|
||||
throws SSHException {
|
||||
super.notifyDisconnect();
|
||||
FutureUtils.alertAll(new ConnectionException("Disconnected."), globalReqFutures);
|
||||
for (Channel chan : channels.values())
|
||||
chan.close();
|
||||
final ConnectionException ex = new ConnectionException("Disconnected.");
|
||||
FutureUtils.alertAll(ex, globalReqFutures);
|
||||
ErrorNotifiable.Util.alertAll(ex, new HashSet<Channel>(channels.values()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public abstract class AbstractChannel
|
||||
implements Channel {
|
||||
|
||||
/** Logger */
|
||||
protected final Logger log;
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/** Transport layer */
|
||||
protected final Transport trans;
|
||||
@@ -83,7 +83,7 @@ public abstract class AbstractChannel
|
||||
/** Channel open event */
|
||||
protected final Event<ConnectionException> open;
|
||||
/** Channel close event */
|
||||
private final Event<ConnectionException> close;
|
||||
protected final Event<ConnectionException> close;
|
||||
|
||||
/* Access to these fields should be synchronized using this object */
|
||||
private boolean eofSent;
|
||||
@@ -109,9 +109,7 @@ public abstract class AbstractChannel
|
||||
|
||||
id = conn.nextID();
|
||||
|
||||
log = LoggerFactory.getLogger("chan#" + id);
|
||||
|
||||
lwin = new Window.Local(id, conn.getWindowSize(), conn.getMaxPacketSize());
|
||||
lwin = new Window.Local(conn.getWindowSize(), conn.getMaxPacketSize());
|
||||
in = new ChannelInputStream(this, trans, lwin);
|
||||
|
||||
open = new Event<ConnectionException>("chan#" + id + " / " + "open", ConnectionException.chainer, lock);
|
||||
@@ -120,7 +118,7 @@ public abstract class AbstractChannel
|
||||
|
||||
protected void init(int recipient, int remoteWinSize, int remoteMaxPacketSize) {
|
||||
this.recipient = recipient;
|
||||
rwin = new Window.Remote(id, remoteWinSize, remoteMaxPacketSize);
|
||||
rwin = new Window.Remote(remoteWinSize, remoteMaxPacketSize);
|
||||
out = new ChannelOutputStream(this, trans, rwin);
|
||||
log.info("Initialized - {}", this);
|
||||
}
|
||||
@@ -269,6 +267,16 @@ public abstract class AbstractChannel
|
||||
}
|
||||
}
|
||||
|
||||
public void join()
|
||||
throws ConnectionException {
|
||||
close.await();
|
||||
}
|
||||
|
||||
public void join(int timeout, TimeUnit unit)
|
||||
throws ConnectionException {
|
||||
close.await(timeout, unit);
|
||||
}
|
||||
|
||||
protected synchronized void sendClose()
|
||||
throws TransportException {
|
||||
try {
|
||||
@@ -304,7 +312,6 @@ public abstract class AbstractChannel
|
||||
rwin.expand(howMuch);
|
||||
}
|
||||
|
||||
/** Called when this channel's end-of-life has been reached. Subclasses may override but must call super. */
|
||||
protected void finishOff() {
|
||||
conn.forget(this);
|
||||
close.set();
|
||||
@@ -332,7 +339,7 @@ public abstract class AbstractChannel
|
||||
protected void receiveInto(ChannelInputStream stream, SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
final int len = buf.readInt();
|
||||
if (len < 0 || len > getLocalMaxPacketSize() || len != buf.available())
|
||||
if (len < 0 || len > getLocalMaxPacketSize() || len > buf.available())
|
||||
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Bad item length: " + len);
|
||||
if (log.isTraceEnabled())
|
||||
log.trace("IN #{}: {}", id, ByteArrayUtils.printHex(buf.array(), buf.rpos(), len));
|
||||
|
||||
@@ -23,6 +23,7 @@ import net.schmizz.sshj.transport.TransportException;
|
||||
import java.io.Closeable;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** A channel is the basic medium for application-layer data on top of an SSH transport. */
|
||||
public interface Channel
|
||||
@@ -135,4 +136,10 @@ public interface Channel
|
||||
*/
|
||||
void setAutoExpand(boolean autoExpand);
|
||||
|
||||
void join()
|
||||
throws ConnectionException;
|
||||
|
||||
void join(int timeout, TimeUnit unit)
|
||||
throws ConnectionException;
|
||||
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public final class ChannelInputStream
|
||||
extends InputStream
|
||||
implements ErrorNotifiable {
|
||||
|
||||
private final Logger log;
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final Channel chan;
|
||||
private final Transport trans;
|
||||
@@ -71,8 +71,6 @@ public final class ChannelInputStream
|
||||
private SSHException error;
|
||||
|
||||
public ChannelInputStream(Channel chan, Transport trans, Window.Local win) {
|
||||
log = LoggerFactory.getLogger("<< chan#" + chan.getID() + " / input stream >>");
|
||||
|
||||
this.chan = chan;
|
||||
this.trans = trans;
|
||||
this.win = win;
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class Window {
|
||||
|
||||
protected final Logger log;
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@@ -30,8 +30,7 @@ public abstract class Window {
|
||||
|
||||
protected int size;
|
||||
|
||||
public Window(int chanID, String kindOfWindow, int initialWinSize, int maxPacketSize) {
|
||||
log = LoggerFactory.getLogger("<< chan#" + chanID + " / " + kindOfWindow + " >>");
|
||||
public Window(int initialWinSize, int maxPacketSize) {
|
||||
size = initialWinSize;
|
||||
this.maxPacketSize = maxPacketSize;
|
||||
}
|
||||
@@ -70,8 +69,8 @@ public abstract class Window {
|
||||
public static final class Remote
|
||||
extends Window {
|
||||
|
||||
public Remote(int chanID, int initialWinSize, int maxPacketSize) {
|
||||
super(chanID, "remote win", initialWinSize, maxPacketSize);
|
||||
public Remote(int initialWinSize, int maxPacketSize) {
|
||||
super(initialWinSize, maxPacketSize);
|
||||
}
|
||||
|
||||
public void waitAndConsume(int howMuch)
|
||||
@@ -98,8 +97,8 @@ public abstract class Window {
|
||||
private final int initialSize;
|
||||
private final int threshold;
|
||||
|
||||
public Local(int chanID, int initialWinSize, int maxPacketSize) {
|
||||
super(chanID, "local win", initialWinSize, maxPacketSize);
|
||||
public Local(int initialWinSize, int maxPacketSize) {
|
||||
super(initialWinSize, maxPacketSize);
|
||||
this.initialSize = initialWinSize;
|
||||
threshold = Math.min(maxPacketSize * 20, initialSize / 4);
|
||||
}
|
||||
|
||||
@@ -35,10 +35,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.connection.channel.direct;
|
||||
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.common.StreamCopier;
|
||||
import net.schmizz.sshj.common.*;
|
||||
import net.schmizz.sshj.connection.Connection;
|
||||
import net.schmizz.sshj.connection.ConnectionException;
|
||||
import net.schmizz.sshj.connection.channel.ChannelInputStream;
|
||||
@@ -50,9 +47,10 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** {@link Session} implementation. */
|
||||
public class
|
||||
SessionChannel
|
||||
/**
|
||||
* {@link Session} implementation.
|
||||
*/
|
||||
public class SessionChannel
|
||||
extends AbstractDirectChannel
|
||||
implements Session, Session.Command, Session.Shell, Session.Subsystem {
|
||||
|
||||
@@ -66,6 +64,8 @@ public class
|
||||
|
||||
private Boolean canDoFlowControl;
|
||||
|
||||
private boolean usedUp;
|
||||
|
||||
public SessionChannel(Connection conn) {
|
||||
super(conn, "session");
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class
|
||||
@Override
|
||||
public void allocateDefaultPTY()
|
||||
throws ConnectionException, TransportException {
|
||||
allocatePTY("vt100", 80, 24, 0, 0, Collections.<PTYMode, Integer>emptyMap());
|
||||
allocatePTY("dummy", 80, 24, 0, 0, Collections.<PTYMode, Integer>emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -114,9 +114,11 @@ public class
|
||||
@Override
|
||||
public Command exec(String command)
|
||||
throws ConnectionException, TransportException {
|
||||
checkReuse();
|
||||
log.info("Will request to exec `{}`", command);
|
||||
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command))
|
||||
.await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
usedUp = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -170,8 +172,7 @@ public class
|
||||
|
||||
@Override
|
||||
public void reqX11Forwarding(String authProto, String authCookie, int screen)
|
||||
throws ConnectionException,
|
||||
TransportException {
|
||||
throws ConnectionException, TransportException {
|
||||
sendChannelRequest(
|
||||
"x11-req",
|
||||
true,
|
||||
@@ -199,16 +200,20 @@ public class
|
||||
@Override
|
||||
public Shell startShell()
|
||||
throws ConnectionException, TransportException {
|
||||
checkReuse();
|
||||
sendChannelRequest("shell", true, null).await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
usedUp = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Subsystem startSubsystem(String name)
|
||||
throws ConnectionException, TransportException {
|
||||
checkReuse();
|
||||
log.info("Will request `{}` subsystem", name);
|
||||
sendChannelRequest("subsystem", true, new Buffer.PlainBuffer().putString(name))
|
||||
.await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
usedUp = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -238,4 +243,15 @@ public class
|
||||
super.gotExtendedData(dataTypeCode, buf);
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public void notifyError(SSHException error) {
|
||||
err.notifyError(error);
|
||||
super.notifyError(error);
|
||||
}
|
||||
|
||||
private void checkReuse() {
|
||||
if (usedUp)
|
||||
throw new SSHRuntimeException("This session channel is all used up");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,21 +19,20 @@ package net.schmizz.sshj.connection.channel.direct;
|
||||
/** Various signals that may be sent or received. The signals are from POSIX and simply miss the {@code "SIG_"} prefix. */
|
||||
public enum Signal {
|
||||
|
||||
ABRT("ABRT"),
|
||||
ALRM("ALRM"),
|
||||
FPE("FPE"),
|
||||
HUP("HUP"),
|
||||
ILL("ILL"),
|
||||
INT("INT"),
|
||||
KILL("KILL"),
|
||||
PIPE("PIPE"),
|
||||
QUIT(
|
||||
"QUIT"),
|
||||
SEGV("SEGV"),
|
||||
TERM("TERM"),
|
||||
USR1("USR1"),
|
||||
USR2("USR2"),
|
||||
UNKNOWN("UNKNOWN");
|
||||
ABRT,
|
||||
ALRM,
|
||||
FPE,
|
||||
HUP,
|
||||
ILL,
|
||||
INT,
|
||||
KILL,
|
||||
PIPE,
|
||||
QUIT,
|
||||
SEGV,
|
||||
TERM,
|
||||
USR1,
|
||||
USR2,
|
||||
UNKNOWN;
|
||||
|
||||
/**
|
||||
* Create from the string representation used when the signal is received as part of an SSH packet.
|
||||
@@ -44,20 +43,9 @@ public enum Signal {
|
||||
*/
|
||||
public static Signal fromString(String name) {
|
||||
for (Signal sig : Signal.values())
|
||||
if (sig.name.equals(name))
|
||||
if (sig.toString().equals(name))
|
||||
return sig;
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
private final String name;
|
||||
|
||||
private Signal(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,9 +34,11 @@ public class PacketReader
|
||||
private final Map<Long, Future<Response, SFTPException>> futures = new ConcurrentHashMap<Long, Future<Response, SFTPException>>();
|
||||
private final SFTPPacket<Response> packet = new SFTPPacket<Response>();
|
||||
private final byte[] lenBuf = new byte[4];
|
||||
private final SFTPEngine engine;
|
||||
|
||||
public PacketReader(InputStream in) {
|
||||
this.in = in;
|
||||
public PacketReader(SFTPEngine engine) {
|
||||
this.engine = engine;
|
||||
this.in = engine.getSubsystem().getInputStream();
|
||||
setName("sftp reader");
|
||||
}
|
||||
|
||||
@@ -78,7 +80,7 @@ public class PacketReader
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
while (!isInterrupted()) {
|
||||
readPacket();
|
||||
handle();
|
||||
}
|
||||
@@ -90,7 +92,7 @@ public class PacketReader
|
||||
|
||||
public void handle()
|
||||
throws SFTPException {
|
||||
Response resp = new Response(packet);
|
||||
Response resp = new Response(packet, engine.getOperativeProtocolVersion());
|
||||
Future<Response, SFTPException> future = futures.remove(resp.getRequestID());
|
||||
log.debug("Received {} packet", resp.getType());
|
||||
if (future == null)
|
||||
|
||||
@@ -52,8 +52,7 @@ public enum PacketType {
|
||||
|
||||
static {
|
||||
for (PacketType t : PacketType.values())
|
||||
if (cache[t.toByte() & 0xff] == null)
|
||||
cache[t.toByte() & 0xff] = t;
|
||||
cache[t.toByte() & 0xff] = t;
|
||||
}
|
||||
|
||||
private PacketType(int b) {
|
||||
|
||||
@@ -19,11 +19,11 @@ import java.io.IOException;
|
||||
|
||||
public class PathHelper {
|
||||
|
||||
private final SFTPEngine sftp;
|
||||
private final SFTPEngine engine;
|
||||
private String dotDir;
|
||||
|
||||
public PathHelper(SFTPEngine sftp) {
|
||||
this.sftp = sftp;
|
||||
public PathHelper(SFTPEngine engine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
public PathComponents getComponents(String path)
|
||||
@@ -56,7 +56,7 @@ public class PathHelper {
|
||||
|
||||
private String canon(String path)
|
||||
throws IOException {
|
||||
return sftp.canonicalize(path);
|
||||
return engine.canonicalize(path);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import org.slf4j.LoggerFactory;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
abstract class RemoteResource
|
||||
public abstract class RemoteResource
|
||||
implements Closeable {
|
||||
|
||||
/** Logger */
|
||||
|
||||
@@ -47,15 +47,21 @@ public class Response
|
||||
|
||||
}
|
||||
|
||||
private final int protocolVersion;
|
||||
private final PacketType type;
|
||||
private final long reqID;
|
||||
|
||||
public Response(Buffer<Response> pk) {
|
||||
public Response(Buffer<Response> pk, int protocolVersion) {
|
||||
super(pk);
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.type = readType();
|
||||
this.reqID = readLong();
|
||||
}
|
||||
|
||||
public int getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public long getRequestID() {
|
||||
return reqID;
|
||||
}
|
||||
@@ -72,7 +78,7 @@ public class Response
|
||||
throws SFTPException {
|
||||
if (getType() != pt)
|
||||
if (getType() == PacketType.STATUS)
|
||||
throw new SFTPException(readStatusCode(), readString());
|
||||
error(readStatusCode());
|
||||
else
|
||||
throw new SFTPException("Unexpected packet " + getType());
|
||||
return this;
|
||||
@@ -85,10 +91,15 @@ public class Response
|
||||
|
||||
public Response ensureStatusIs(StatusCode acceptable)
|
||||
throws SFTPException {
|
||||
StatusCode sc = readStatusCode();
|
||||
final StatusCode sc = readStatusCode();
|
||||
if (sc != acceptable)
|
||||
throw new SFTPException(sc, readString());
|
||||
error(sc);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected String error(StatusCode sc)
|
||||
throws SFTPException {
|
||||
throw new SFTPException(sc, protocolVersion < 3 ? sc.toString() : readString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
package net.schmizz.sshj.sftp;
|
||||
|
||||
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
|
||||
import net.schmizz.sshj.xfer.FilePermission;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.Deque;
|
||||
import java.util.EnumSet;
|
||||
@@ -27,24 +27,24 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class SFTPClient {
|
||||
public class SFTPClient
|
||||
implements Closeable {
|
||||
|
||||
/** Logger */
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final SFTPEngine sftp;
|
||||
private final SFTPEngine engine;
|
||||
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);
|
||||
public SFTPClient(SFTPEngine engine) {
|
||||
this.engine = engine;
|
||||
this.pathHelper = new PathHelper(engine);
|
||||
this.xfer = new SFTPFileTransfer(engine);
|
||||
}
|
||||
|
||||
public SFTPEngine getSFTPEngine() {
|
||||
return sftp;
|
||||
return engine;
|
||||
}
|
||||
|
||||
public SFTPFileTransfer getFileTansfer() {
|
||||
@@ -58,7 +58,7 @@ public class SFTPClient {
|
||||
|
||||
public List<RemoteResourceInfo> ls(String path, RemoteResourceFilter filter)
|
||||
throws IOException {
|
||||
final RemoteDirectory dir = sftp.openDir(path);
|
||||
final RemoteDirectory dir = engine.openDir(path);
|
||||
try {
|
||||
return dir.scan(filter);
|
||||
} finally {
|
||||
@@ -69,7 +69,7 @@ public class SFTPClient {
|
||||
public RemoteFile open(String filename, Set<OpenMode> mode, FileAttributes attrs)
|
||||
throws IOException {
|
||||
log.debug("Opening `{}`", filename);
|
||||
return sftp.open(filename, mode, attrs);
|
||||
return engine.open(filename, mode, attrs);
|
||||
}
|
||||
|
||||
public RemoteFile open(String filename, Set<OpenMode> mode)
|
||||
@@ -84,7 +84,7 @@ public class SFTPClient {
|
||||
|
||||
public void mkdir(String dirname)
|
||||
throws IOException {
|
||||
sftp.makeDir(dirname);
|
||||
engine.makeDir(dirname);
|
||||
}
|
||||
|
||||
public void mkdirs(String path)
|
||||
@@ -109,7 +109,7 @@ public class SFTPClient {
|
||||
public FileAttributes statExistence(String path)
|
||||
throws IOException {
|
||||
try {
|
||||
return sftp.stat(path);
|
||||
return engine.stat(path);
|
||||
} catch (SFTPException sftpe) {
|
||||
if (sftpe.getStatusCode() == Response.StatusCode.NO_SUCH_FILE) {
|
||||
return null;
|
||||
@@ -121,31 +121,31 @@ public class SFTPClient {
|
||||
|
||||
public void rename(String oldpath, String newpath)
|
||||
throws IOException {
|
||||
sftp.rename(oldpath, newpath);
|
||||
engine.rename(oldpath, newpath);
|
||||
}
|
||||
|
||||
public void rm(String filename)
|
||||
throws IOException {
|
||||
sftp.remove(filename);
|
||||
engine.remove(filename);
|
||||
}
|
||||
|
||||
public void rmdir(String dirname)
|
||||
throws IOException {
|
||||
sftp.removeDir(dirname);
|
||||
engine.removeDir(dirname);
|
||||
}
|
||||
|
||||
public void symlink(String linkpath, String targetpath)
|
||||
throws IOException {
|
||||
sftp.symlink(linkpath, targetpath);
|
||||
engine.symlink(linkpath, targetpath);
|
||||
}
|
||||
|
||||
public int version() {
|
||||
return sftp.getOperativeProtocolVersion();
|
||||
return engine.getOperativeProtocolVersion();
|
||||
}
|
||||
|
||||
public void setattr(String path, FileAttributes attrs)
|
||||
throws IOException {
|
||||
sftp.setAttributes(path, attrs);
|
||||
engine.setAttributes(path, attrs);
|
||||
}
|
||||
|
||||
public int uid(String path)
|
||||
@@ -185,17 +185,17 @@ public class SFTPClient {
|
||||
|
||||
public String readlink(String path)
|
||||
throws IOException {
|
||||
return sftp.readLink(path);
|
||||
return engine.readLink(path);
|
||||
}
|
||||
|
||||
public FileAttributes stat(String path)
|
||||
throws IOException {
|
||||
return sftp.stat(path);
|
||||
return engine.stat(path);
|
||||
}
|
||||
|
||||
public FileAttributes lstat(String path)
|
||||
throws IOException {
|
||||
return sftp.lstat(path);
|
||||
return engine.lstat(path);
|
||||
}
|
||||
|
||||
public void chown(String path, int uid)
|
||||
@@ -220,7 +220,7 @@ public class SFTPClient {
|
||||
|
||||
public String canonicalize(String path)
|
||||
throws IOException {
|
||||
return sftp.canonicalize(path);
|
||||
return engine.canonicalize(path);
|
||||
}
|
||||
|
||||
public long size(String path)
|
||||
@@ -238,4 +238,10 @@ public class SFTPClient {
|
||||
xfer.upload(source, dest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException {
|
||||
engine.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import net.schmizz.sshj.connection.channel.direct.SessionFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.EnumSet;
|
||||
@@ -30,39 +31,34 @@ import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SFTPEngine
|
||||
implements Requester {
|
||||
|
||||
/** Logger */
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
public static final int PROTOCOL_VERSION = 3;
|
||||
implements Requester, Closeable {
|
||||
|
||||
public static final int MAX_SUPPORTED_VERSION = 3;
|
||||
public static final int DEFAULT_TIMEOUT = 30;
|
||||
|
||||
private volatile int timeout = DEFAULT_TIMEOUT;
|
||||
/** Logger */
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final Subsystem sub;
|
||||
private final PacketReader reader;
|
||||
private final OutputStream out;
|
||||
protected volatile int timeout = DEFAULT_TIMEOUT;
|
||||
|
||||
private long reqID;
|
||||
private int negotiatedVersion;
|
||||
private final Map<String, String> serverExtensions = new HashMap<String, String>();
|
||||
protected final Subsystem sub;
|
||||
protected final PacketReader reader;
|
||||
protected final OutputStream out;
|
||||
|
||||
protected long reqID;
|
||||
protected int operativeVersion;
|
||||
protected final Map<String, String> serverExtensions = new HashMap<String, String>();
|
||||
|
||||
public SFTPEngine(SessionFactory ssh)
|
||||
throws SSHException {
|
||||
sub = ssh.startSession().startSubsystem("sftp");
|
||||
out = sub.getOutputStream();
|
||||
reader = new PacketReader(sub.getInputStream());
|
||||
}
|
||||
|
||||
public Subsystem getSubsystem() {
|
||||
return sub;
|
||||
reader = new PacketReader(this);
|
||||
}
|
||||
|
||||
public SFTPEngine init()
|
||||
throws IOException {
|
||||
transmit(new SFTPPacket<Request>(PacketType.INIT).putInt(PROTOCOL_VERSION));
|
||||
transmit(new SFTPPacket<Request>(PacketType.INIT).putInt(MAX_SUPPORTED_VERSION));
|
||||
|
||||
final SFTPPacket<Response> response = reader.readPacket();
|
||||
|
||||
@@ -70,10 +66,10 @@ public class SFTPEngine
|
||||
if (type != PacketType.VERSION)
|
||||
throw new SFTPException("Expected INIT packet, received: " + type);
|
||||
|
||||
negotiatedVersion = response.readInt();
|
||||
log.info("Client version {}, server version {}", PROTOCOL_VERSION, negotiatedVersion);
|
||||
if (negotiatedVersion < PROTOCOL_VERSION)
|
||||
throw new SFTPException("Server reported protocol version: " + negotiatedVersion);
|
||||
operativeVersion = response.readInt();
|
||||
log.info("Server version {}", operativeVersion);
|
||||
if (MAX_SUPPORTED_VERSION < operativeVersion)
|
||||
throw new SFTPException("Server reported incompatible protocol version: " + operativeVersion);
|
||||
|
||||
while (response.available() > 0)
|
||||
serverExtensions.put(response.readString(), response.readString());
|
||||
@@ -83,8 +79,16 @@ public class SFTPEngine
|
||||
return this;
|
||||
}
|
||||
|
||||
public Subsystem getSubsystem() {
|
||||
return sub;
|
||||
}
|
||||
|
||||
public int getOperativeProtocolVersion() {
|
||||
return negotiatedVersion;
|
||||
return operativeVersion;
|
||||
}
|
||||
|
||||
public Request newExtendedRequest(String reqName) {
|
||||
return newRequest(PacketType.EXTENDED).putString(reqName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,17 +105,6 @@ public class SFTPEngine
|
||||
return req.getResponseFuture().get(timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private synchronized void transmit(SFTPPacket<Request> payload)
|
||||
throws IOException {
|
||||
final int len = payload.available();
|
||||
out.write((len >>> 24) & 0xff);
|
||||
out.write((len >>> 16) & 0xff);
|
||||
out.write((len >>> 8) & 0xff);
|
||||
out.write(len & 0xff);
|
||||
out.write(payload.array(), payload.rpos(), len);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public RemoteFile open(String path, Set<OpenMode> modes, FileAttributes fa)
|
||||
throws IOException {
|
||||
final String handle = doRequest(
|
||||
@@ -147,6 +140,8 @@ public class SFTPEngine
|
||||
|
||||
public String readLink(String path)
|
||||
throws IOException {
|
||||
if (operativeVersion < 3)
|
||||
throw new SFTPException("READLINK is not supported in SFTPv" + operativeVersion);
|
||||
return readSingleName(
|
||||
doRequest(
|
||||
newRequest(PacketType.READLINK).putString(path)
|
||||
@@ -165,6 +160,8 @@ public class SFTPEngine
|
||||
|
||||
public void symlink(String linkpath, String targetpath)
|
||||
throws IOException {
|
||||
if (operativeVersion < 3)
|
||||
throw new SFTPException("SYMLINK is not supported in SFTPv" + operativeVersion);
|
||||
doRequest(
|
||||
newRequest(PacketType.SYMLINK).putString(linkpath).putString(targetpath)
|
||||
).ensureStatusPacketIsOK();
|
||||
@@ -184,13 +181,6 @@ public class SFTPEngine
|
||||
).ensureStatusIs(Response.StatusCode.OK);
|
||||
}
|
||||
|
||||
private FileAttributes stat(PacketType pt, String path)
|
||||
throws IOException {
|
||||
return doRequest(newRequest(pt).putString(path))
|
||||
.ensurePacketTypeIs(PacketType.ATTRS)
|
||||
.readFileAttributes();
|
||||
}
|
||||
|
||||
public FileAttributes stat(String path)
|
||||
throws IOException {
|
||||
return stat(PacketType.STAT, path);
|
||||
@@ -203,6 +193,8 @@ public class SFTPEngine
|
||||
|
||||
public void rename(String oldPath, String newPath)
|
||||
throws IOException {
|
||||
if (operativeVersion < 1)
|
||||
throw new SFTPException("RENAME is not supported in SFTPv" + operativeVersion);
|
||||
doRequest(
|
||||
newRequest(PacketType.RENAME).putString(oldPath).putString(newPath)
|
||||
).ensureStatusPacketIsOK();
|
||||
@@ -216,15 +208,6 @@ public class SFTPEngine
|
||||
));
|
||||
}
|
||||
|
||||
private static String readSingleName(Response res)
|
||||
throws IOException {
|
||||
res.ensurePacketTypeIs(PacketType.NAME);
|
||||
if (res.readInt() == 1)
|
||||
return res.readString();
|
||||
else
|
||||
throw new SFTPException("Unexpected data in " + res.getType() + " packet");
|
||||
}
|
||||
|
||||
public void setTimeout(int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
@@ -233,4 +216,37 @@ public class SFTPEngine
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException {
|
||||
sub.close();
|
||||
}
|
||||
|
||||
protected FileAttributes stat(PacketType pt, String path)
|
||||
throws IOException {
|
||||
return doRequest(newRequest(pt).putString(path))
|
||||
.ensurePacketTypeIs(PacketType.ATTRS)
|
||||
.readFileAttributes();
|
||||
}
|
||||
|
||||
protected static String readSingleName(Response res)
|
||||
throws IOException {
|
||||
res.ensurePacketTypeIs(PacketType.NAME);
|
||||
if (res.readInt() == 1)
|
||||
return res.readString();
|
||||
else
|
||||
throw new SFTPException("Unexpected data in " + res.getType() + " packet");
|
||||
}
|
||||
|
||||
protected synchronized void transmit(SFTPPacket<Request> payload)
|
||||
throws IOException {
|
||||
final int len = payload.available();
|
||||
out.write((len >>> 24) & 0xff);
|
||||
out.write((len >>> 16) & 0xff);
|
||||
out.write((len >>> 8) & 0xff);
|
||||
out.write(len & 0xff);
|
||||
out.write(payload.array(), payload.rpos(), len);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ public class SFTPException
|
||||
|
||||
public StatusCode getStatusCode() {
|
||||
return (sc == null) ? StatusCode.UNKNOWN : sc;
|
||||
|
||||
}
|
||||
|
||||
public SFTPException(StatusCode sc, String msg) {
|
||||
|
||||
@@ -33,7 +33,7 @@ public class SFTPFileTransfer
|
||||
extends AbstractFileTransfer
|
||||
implements FileTransfer {
|
||||
|
||||
private final SFTPEngine sftp;
|
||||
private final SFTPEngine engine;
|
||||
private final PathHelper pathHelper;
|
||||
|
||||
private volatile FileFilter uploadFilter = defaultLocalFilter;
|
||||
@@ -53,9 +53,9 @@ public class SFTPFileTransfer
|
||||
}
|
||||
};
|
||||
|
||||
public SFTPFileTransfer(SFTPEngine sftp) {
|
||||
this.sftp = sftp;
|
||||
this.pathHelper = new PathHelper(sftp);
|
||||
public SFTPFileTransfer(SFTPEngine engine) {
|
||||
this.engine = engine;
|
||||
this.pathHelper = new PathHelper(engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,7 +68,7 @@ public class SFTPFileTransfer
|
||||
public void download(String source, String dest)
|
||||
throws IOException {
|
||||
final PathComponents pathComponents = pathHelper.getComponents(source);
|
||||
final FileAttributes attributes = sftp.stat(source);
|
||||
final FileAttributes attributes = engine.stat(source);
|
||||
new Downloader().download(new RemoteResourceInfo(pathComponents, attributes), new File(dest));
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ public class SFTPFileTransfer
|
||||
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());
|
||||
final RemoteDirectory rd = engine.openDir(remote.getPath());
|
||||
try {
|
||||
for (RemoteResourceInfo rri : rd.scan(getDownloadFilter()))
|
||||
download(rri, new File(adjusted.getPath(), rri.getName()));
|
||||
@@ -132,11 +132,11 @@ public class SFTPFileTransfer
|
||||
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());
|
||||
final RemoteFile rf = engine.open(remote.getPath());
|
||||
try {
|
||||
final FileOutputStream fos = new FileOutputStream(adjusted);
|
||||
try {
|
||||
StreamCopier.copy(rf.getInputStream(), fos, sftp.getSubsystem()
|
||||
StreamCopier.copy(rf.getInputStream(), fos, engine.getSubsystem()
|
||||
.getLocalMaxPacketSize(), false, listener);
|
||||
} finally {
|
||||
fos.close();
|
||||
@@ -176,7 +176,7 @@ public class SFTPFileTransfer
|
||||
listener.finishedFile();
|
||||
} else
|
||||
throw new IOException(local + " is not a file or directory");
|
||||
sftp.setAttributes(adjustedPath, getAttributes(local));
|
||||
engine.setAttributes(adjustedPath, getAttributes(local));
|
||||
}
|
||||
|
||||
private String uploadDir(File local, String remote)
|
||||
@@ -190,13 +190,13 @@ public class SFTPFileTransfer
|
||||
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));
|
||||
final RemoteFile rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE,
|
||||
OpenMode.CREAT,
|
||||
OpenMode.TRUNC));
|
||||
try {
|
||||
final FileInputStream fis = new FileInputStream(local);
|
||||
try {
|
||||
final int bufSize = sftp.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead();
|
||||
final int bufSize = engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead();
|
||||
StreamCopier.copy(fis, rf.getOutputStream(), bufSize, false, listener);
|
||||
} finally {
|
||||
fis.close();
|
||||
@@ -211,11 +211,11 @@ public class SFTPFileTransfer
|
||||
throws IOException {
|
||||
final FileAttributes attrs;
|
||||
try {
|
||||
attrs = sftp.stat(remote);
|
||||
attrs = engine.stat(remote);
|
||||
} catch (SFTPException e) {
|
||||
if (e.getStatusCode() == StatusCode.NO_SUCH_FILE) {
|
||||
log.debug("probeDir: {} does not exist, creating", remote);
|
||||
sftp.makeDir(remote);
|
||||
engine.makeDir(remote);
|
||||
return remote;
|
||||
} else
|
||||
throw e;
|
||||
@@ -237,7 +237,7 @@ public class SFTPFileTransfer
|
||||
throws IOException {
|
||||
final FileAttributes attrs;
|
||||
try {
|
||||
attrs = sftp.stat(remote);
|
||||
attrs = engine.stat(remote);
|
||||
} catch (SFTPException e) {
|
||||
if (e.getStatusCode() == StatusCode.NO_SUCH_FILE) {
|
||||
log.debug("probeFile: {} does not exist", remote);
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package net.schmizz.sshj.sftp;
|
||||
|
||||
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -26,9 +24,9 @@ public class StatefulSFTPClient
|
||||
|
||||
private String cwd;
|
||||
|
||||
public StatefulSFTPClient(SessionFactory ssh)
|
||||
public StatefulSFTPClient(SFTPEngine engine)
|
||||
throws IOException {
|
||||
super(ssh);
|
||||
super(engine);
|
||||
this.cwd = getSFTPEngine().canonicalize(".");
|
||||
log.info("Start dir = " + cwd);
|
||||
}
|
||||
@@ -183,7 +181,7 @@ public class StatefulSFTPClient
|
||||
@Override
|
||||
public void put(String source, String dest)
|
||||
throws IOException {
|
||||
super.get(source, cwdify(dest));
|
||||
super.put(source, cwdify(dest));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,8 +50,6 @@ final class Heartbeater
|
||||
|
||||
private int interval;
|
||||
|
||||
private boolean started;
|
||||
|
||||
Heartbeater(TransportImpl trans) {
|
||||
this.trans = trans;
|
||||
setName("heartbeater");
|
||||
@@ -59,36 +57,36 @@ final class Heartbeater
|
||||
|
||||
synchronized void setInterval(int interval) {
|
||||
this.interval = interval;
|
||||
if (interval != 0) {
|
||||
if (!started)
|
||||
start();
|
||||
notify();
|
||||
}
|
||||
if (interval > 0 && getState() == Thread.State.NEW)
|
||||
start();
|
||||
notify();
|
||||
}
|
||||
|
||||
synchronized int getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
synchronized private int getPositiveInterval()
|
||||
throws InterruptedException {
|
||||
while (interval <= 0)
|
||||
wait();
|
||||
return interval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
log.debug("Starting");
|
||||
try {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
int hi;
|
||||
synchronized (this) {
|
||||
while ((hi = interval) == 0)
|
||||
wait();
|
||||
}
|
||||
if (!started)
|
||||
started = true;
|
||||
else if (trans.isRunning()) {
|
||||
while (!isInterrupted()) {
|
||||
final int hi = getPositiveInterval();
|
||||
if (trans.isRunning()) {
|
||||
log.info("Sending heartbeat since {} seconds elapsed", hi);
|
||||
trans.write(new SSHPacket(Message.IGNORE));
|
||||
}
|
||||
Thread.sleep(hi * 1000);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
if (isInterrupted()) {
|
||||
// We are meant to shut up and draw to a close if interrupted
|
||||
} else
|
||||
trans.die(e);
|
||||
@@ -96,4 +94,5 @@ final class Heartbeater
|
||||
|
||||
log.debug("Stopping");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -92,8 +92,7 @@ final class KeyExchanger
|
||||
private Proposal clientProposal;
|
||||
private NegotiatedAlgorithms negotiatedAlgs;
|
||||
|
||||
private final Event<TransportException> kexInitSent = new Event<TransportException>("kexinit sent",
|
||||
TransportException.chainer);
|
||||
private final Event<TransportException> kexInitSent = new Event<TransportException>("kexinit sent", TransportException.chainer);
|
||||
|
||||
private final Event<TransportException> done;
|
||||
|
||||
|
||||
@@ -41,22 +41,20 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
final class Reader
|
||||
public final class Reader
|
||||
extends Thread {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final TransportImpl trans;
|
||||
|
||||
Reader(TransportImpl trans) {
|
||||
public Reader(TransportImpl trans) {
|
||||
this.trans = trans;
|
||||
setName("reader");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Thread curThread = Thread.currentThread();
|
||||
|
||||
try {
|
||||
|
||||
final Decoder decoder = trans.getDecoder();
|
||||
@@ -66,7 +64,7 @@ final class Reader
|
||||
|
||||
int needed = 1;
|
||||
|
||||
while (!curThread.isInterrupted()) {
|
||||
while (!isInterrupted()) {
|
||||
int read = inp.read(recvbuf, 0, needed);
|
||||
if (read == -1)
|
||||
throw new TransportException("Broken transport; encountered EOF");
|
||||
@@ -75,7 +73,7 @@ final class Reader
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
if (curThread.isInterrupted()) {
|
||||
if (isInterrupted()) {
|
||||
// We are meant to shut up and draw to a close if interrupted
|
||||
} else
|
||||
trans.die(e);
|
||||
|
||||
@@ -421,8 +421,8 @@ public final class TransportImpl
|
||||
.putInt(reason.toInt())
|
||||
.putString(message)
|
||||
.putString(""));
|
||||
} catch (IOException logged) {
|
||||
log.warn("Error writing packet: {}", logged);
|
||||
} catch (IOException worthless) {
|
||||
log.debug("Error writing packet: {}", worthless.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ public class UserAuthImpl
|
||||
}
|
||||
|
||||
private void saveException(UserAuthException e) {
|
||||
log.error("Saving for later - {}", e.toString());
|
||||
log.debug("Saving for later - {}", e.toString());
|
||||
savedEx.push(e);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,6 @@ import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.userauth.UserAuthException;
|
||||
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
|
||||
|
||||
// TODO check if this even works...!
|
||||
|
||||
/** Implements the {@code hostbased} SSH authentication method. */
|
||||
public class AuthHostbased
|
||||
extends KeyedAuthMethod {
|
||||
@@ -28,22 +26,17 @@ public class AuthHostbased
|
||||
protected final String hostname;
|
||||
protected final String hostuser;
|
||||
|
||||
public AuthHostbased(KeyProvider kProv, String hostuser) {
|
||||
this(kProv, hostuser, null);
|
||||
}
|
||||
|
||||
public AuthHostbased(KeyProvider kProv, String hostuser, String hostname) {
|
||||
public AuthHostbased(KeyProvider kProv, String hostname, String hostuser) {
|
||||
super("hostbased", kProv);
|
||||
assert hostuser != null;
|
||||
this.hostuser = hostuser;
|
||||
this.hostname = hostname;
|
||||
this.hostuser = hostuser;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSHPacket buildReq()
|
||||
throws UserAuthException {
|
||||
SSHPacket req = putPubKey(super.buildReq());
|
||||
req.putString(hostname == null ? params.getTransport().getRemoteHost() : hostname).putString(hostuser);
|
||||
final SSHPacket req = putPubKey(super.buildReq());
|
||||
req.putString(hostname).putString(hostuser);
|
||||
return putSig(req);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ public abstract class KeyedAuthMethod
|
||||
// public key as 2 strings: [ key type | key blob ]
|
||||
reqBuf.putString(KeyType.fromKey(key).toString())
|
||||
.putString(new Buffer.PlainBuffer().putPublicKey(key).getCompactData());
|
||||
|
||||
return reqBuf;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package net.schmizz.sshj.xfer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -26,6 +29,8 @@ import java.io.IOException;
|
||||
public class DefaultModeSetter
|
||||
implements ModeSetter {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
public void setLastAccessedTime(File f, long t)
|
||||
throws IOException {
|
||||
@@ -36,7 +41,7 @@ public class DefaultModeSetter
|
||||
public void setLastModifiedTime(File f, long t)
|
||||
throws IOException {
|
||||
if (!f.setLastModified(t * 1000))
|
||||
throw new IOException("Error setting last modified time for " + f);
|
||||
log.warn("Could not set last modified time for {} to {}", f, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,7 +54,7 @@ public class DefaultModeSetter
|
||||
final boolean x = f.setExecutable(FilePermission.USR_X.isIn(perms),
|
||||
!(FilePermission.OTH_X.isIn(perms) || FilePermission.GRP_X.isIn(perms)));
|
||||
if (!(r && w && x))
|
||||
throw new IOException("Error setting permissions for " + f);
|
||||
log.warn("Could not set permissions for {} to {}", f, Integer.toString(perms, 16));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -68,13 +68,13 @@ public enum FilePermission {
|
||||
}
|
||||
|
||||
public boolean isIn(int mask) {
|
||||
return (mask & val) == mask;
|
||||
return (mask & val) == val;
|
||||
}
|
||||
|
||||
public static Set<FilePermission> fromMask(int mask) {
|
||||
List<FilePermission> perms = new LinkedList<FilePermission>();
|
||||
final List<FilePermission> perms = new LinkedList<FilePermission>();
|
||||
for (FilePermission p : FilePermission.values())
|
||||
if ((mask & p.val) == p.val)
|
||||
if (p.isIn(mask))
|
||||
perms.add(p);
|
||||
return new HashSet<FilePermission>(perms);
|
||||
}
|
||||
|
||||
@@ -123,9 +123,6 @@ public final class SCPDownloadClient
|
||||
return true;
|
||||
|
||||
case (char) 1:
|
||||
addWarning(msg.substring(1));
|
||||
break;
|
||||
|
||||
case (char) 2:
|
||||
throw new SCPException("Remote SCP command returned error: " + msg.substring(1));
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ abstract class SCPEngine {
|
||||
|
||||
final SessionFactory host;
|
||||
final TransferListener listener;
|
||||
final Queue<String> warnings = new LinkedList<String>();
|
||||
|
||||
Command scp;
|
||||
int exitStatus;
|
||||
@@ -86,19 +85,6 @@ abstract class SCPEngine {
|
||||
return exitStatus;
|
||||
}
|
||||
|
||||
public Queue<String> getWarnings() {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
public boolean hadWarnings() {
|
||||
return !warnings.isEmpty();
|
||||
}
|
||||
|
||||
void addWarning(String warning) {
|
||||
log.warn(warning);
|
||||
warnings.add(warning);
|
||||
}
|
||||
|
||||
void check(String what)
|
||||
throws IOException {
|
||||
int code = scp.getInputStream().read();
|
||||
@@ -111,9 +97,7 @@ abstract class SCPEngine {
|
||||
case 0: // OK
|
||||
log.debug(what);
|
||||
return;
|
||||
case 1:
|
||||
addWarning(readMessage());
|
||||
break;
|
||||
case 1: // Warning? not
|
||||
case 2:
|
||||
throw new SCPException("Remote SCP command had error: " + readMessage());
|
||||
default:
|
||||
@@ -123,7 +107,6 @@ abstract class SCPEngine {
|
||||
|
||||
void cleanSlate() {
|
||||
exitStatus = -1;
|
||||
warnings.clear();
|
||||
}
|
||||
|
||||
void execSCPWith(List<Arg> args, String path)
|
||||
|
||||
@@ -57,7 +57,7 @@ public class SmokeTest {
|
||||
sshd.start();
|
||||
|
||||
ssh = new SSHClient();
|
||||
ssh.addHostKeyVerifier(hostname, port, fingerprint);
|
||||
ssh.addHostKeyVerifier(fingerprint);
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
Reference in New Issue
Block a user