mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 15:20:54 +03:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20e2161022 | ||
|
|
fb0f3afa17 | ||
|
|
114c2bb424 | ||
|
|
079bde5dbf | ||
|
|
eaee42b017 | ||
|
|
8b61d96808 | ||
|
|
73fcc81e83 | ||
|
|
0f7926d4fa | ||
|
|
ca6f15650a | ||
|
|
eb78dc499d | ||
|
|
a852f33a15 | ||
|
|
ccabc1a20c | ||
|
|
cb2986d32e | ||
|
|
dc70f08e45 | ||
|
|
bf68ec18b2 | ||
|
|
7e78260ca9 | ||
|
|
27c60cee60 | ||
|
|
551b8b4fcf | ||
|
|
fd591e70be | ||
|
|
d177b239c6 | ||
|
|
adf44e2dc0 | ||
|
|
7810b5f653 | ||
|
|
3695e2a184 | ||
|
|
17d8e91f05 | ||
|
|
3c3715eccf | ||
|
|
2ff9f2ae50 | ||
|
|
4f7b29da0d | ||
|
|
2d49cb4d77 | ||
|
|
d752bc36ff | ||
|
|
99e24b7323 | ||
|
|
40b401406c | ||
|
|
803b154505 | ||
|
|
ff5935af2a | ||
|
|
430ebe27ea | ||
|
|
a0109dd8fa | ||
|
|
85abcb7aad | ||
|
|
4de741359e | ||
|
|
ab705d7f2a | ||
|
|
f89c0cc2f0 | ||
|
|
d8cc271cd3 | ||
|
|
d1043ea288 | ||
|
|
ce930c969b | ||
|
|
a2c82de260 | ||
|
|
2e70b56ba3 | ||
|
|
9761f44cd4 | ||
|
|
137dc5ed42 | ||
|
|
286a22270b | ||
|
|
aa9f4e192f | ||
|
|
41ac277023 | ||
|
|
c56f9997f4 | ||
|
|
b92dece6ec | ||
|
|
2880fe2bc0 | ||
|
|
ce5fad9809 | ||
|
|
38883bf15d |
4
CONTRIBUTORS
Normal file
4
CONTRIBUTORS
Normal file
@@ -0,0 +1,4 @@
|
||||
Shikhar Bhushan <shikhar@schmizz.net>
|
||||
Cyril Ledru <cledru@keynectis.net>
|
||||
Incendium <incendium@gmail.com>
|
||||
Philip Langdale <philipl@cloudera.com>
|
||||
2
NOTICE
2
NOTICE
@@ -1,5 +1,5 @@
|
||||
sshj - SSHv2 library for Java
|
||||
Copyright 2010 Shikhar Bhushan
|
||||
Copyright 2010-2011 sshj contributors
|
||||
|
||||
This product includes code derived from software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/):
|
||||
|
||||
19
README.rst
19
README.rst
@@ -1,8 +1,3 @@
|
||||
.. 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
|
||||
==============================
|
||||
|
||||
@@ -16,7 +11,7 @@ Features of the library include:
|
||||
* local and remote port forwarding
|
||||
* scp + complete sftp version 0-3 implementation
|
||||
|
||||
Implementations of the following algorithms are included:
|
||||
Implementations / adapters for the following algorithms are included:
|
||||
|
||||
ciphers
|
||||
``aes{128,192,256}-{cbc,ctr}``, ``blowfish-cbc``, ``3des-cbc``
|
||||
@@ -36,7 +31,7 @@ compression
|
||||
private key files
|
||||
``pkcs8`` encoded (what openssh uses)
|
||||
|
||||
If you need something that is not implemented, it shouldn't be too hard to add (do contribute it!)
|
||||
If you need something that is not included, it shouldn't be too hard to add (do contribute it!)
|
||||
|
||||
|
||||
Dependencies
|
||||
@@ -45,12 +40,6 @@ Dependencies
|
||||
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
|
||||
--------------------
|
||||
|
||||
There is a `google group`_.
|
||||
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
@@ -61,6 +50,4 @@ Fork away!
|
||||
|
||||
.. _bouncycastle: http://www.bouncycastle.org/java.html
|
||||
|
||||
.. _jzlib: http://www.jcraft.com/jzlib/
|
||||
|
||||
.. _`google group`: http://groups.google.com/group/sshj
|
||||
.. _jzlib: http://www.jcraft.com/jzlib/
|
||||
24
pom.xml
24
pom.xml
@@ -6,7 +6,7 @@
|
||||
<groupId>net.schmizz</groupId>
|
||||
<artifactId>sshj</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.3.1</version>
|
||||
<version>0.4.1</version>
|
||||
|
||||
<name>sshj</name>
|
||||
<description>SSHv2 library for Java</description>
|
||||
@@ -42,13 +42,13 @@
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk16</artifactId>
|
||||
<version>1.45</version>
|
||||
<version>1.46</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.8.1</version>
|
||||
<version>4.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -60,19 +60,19 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-core</artifactId>
|
||||
<version>0.4.0</version>
|
||||
<version>0.5.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>0.9.24</version>
|
||||
<version>0.9.29</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>0.9.24</version>
|
||||
<version>0.9.29</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -96,6 +96,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>examples/*.java</exclude>
|
||||
@@ -107,11 +108,14 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
<version>2.1</version>
|
||||
<configuration>
|
||||
<mavenExecutorId>forked-path</mavenExecutorId>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.2-beta-5</version>
|
||||
<version>2.2.1</version>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/assemble/examples.xml</descriptor>
|
||||
@@ -130,6 +134,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.1.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
@@ -142,6 +147,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.8</version>
|
||||
<configuration>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
</configuration>
|
||||
@@ -209,7 +215,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.3</version>
|
||||
<configuration>
|
||||
<passphrase>${gpg.passphrase}</passphrase>
|
||||
</configuration>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
package examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session.Command;
|
||||
|
||||
@@ -36,13 +37,12 @@ public class Exec {
|
||||
final Session session = ssh.startSession();
|
||||
try {
|
||||
final Command cmd = session.exec("ping -c 1 google.com");
|
||||
System.out.print(cmd.getOutputAsString());
|
||||
System.out.println(IOUtils.readFully(cmd.getInputStream()).toString());
|
||||
cmd.join(5, TimeUnit.SECONDS);
|
||||
System.out.println("\n** exit status: " + cmd.getExitStatus());
|
||||
} finally {
|
||||
session.close();
|
||||
}
|
||||
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,11 +46,9 @@ public class RemotePF {
|
||||
// where the server should listen
|
||||
new Forward(8080),
|
||||
// what we do with incoming connections that are forwarded to us
|
||||
new SocketForwardingConnectListener(new InetSocketAddress("google.com", 80)
|
||||
));
|
||||
new SocketForwardingConnectListener(new InetSocketAddress("google.com", 80)));
|
||||
|
||||
client.getTransport()
|
||||
.setHeartbeatInterval(30);
|
||||
client.getTransport().setHeartbeatInterval(30);
|
||||
|
||||
// Something to hang on to so that the forwarding stays
|
||||
client.getTransport().join();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,18 +48,20 @@ class RudimentaryPTY {
|
||||
|
||||
final Shell shell = session.startShell();
|
||||
|
||||
new StreamCopier("stdout", shell.getInputStream(), System.out)
|
||||
new StreamCopier(shell.getInputStream(), System.out)
|
||||
.bufSize(shell.getLocalMaxPacketSize())
|
||||
.start();
|
||||
.spawn("stdout");
|
||||
|
||||
new StreamCopier("stderr", shell.getErrorStream(), System.err)
|
||||
new StreamCopier(shell.getErrorStream(), System.err)
|
||||
.bufSize(shell.getLocalMaxPacketSize())
|
||||
.start();
|
||||
.spawn("stderr");
|
||||
|
||||
// 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(System.in, shell.getOutputStream())
|
||||
.bufSize(shell.getRemoteMaxPacketSize())
|
||||
.copy();
|
||||
|
||||
} finally {
|
||||
session.close();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
package examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.xfer.FileSystemFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -30,9 +31,7 @@ public class SCPDownload {
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
final String src = "test_file";
|
||||
final String target = "/tmp/";
|
||||
ssh.newSCPFileTransfer().download(src, target);
|
||||
ssh.newSCPFileTransfer().download("test_file", new FileSystemFile("/tmp/"));
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
package examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.xfer.FileSystemFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -36,8 +37,7 @@ public class SCPUpload {
|
||||
ssh.useCompression();
|
||||
|
||||
final String src = System.getProperty("user.home") + File.separator + "test_file";
|
||||
final String target = "/tmp/";
|
||||
ssh.newSCPFileTransfer().upload(src, target);
|
||||
ssh.newSCPFileTransfer().upload(new FileSystemFile(src), "/tmp/");
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@ package examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.sftp.SFTPClient;
|
||||
import net.schmizz.sshj.xfer.FileSystemFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -30,11 +31,9 @@ public class SFTPDownload {
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
final String src = "test_file";
|
||||
final String target = "/tmp/";
|
||||
final SFTPClient sftp = ssh.newSFTPClient();
|
||||
try {
|
||||
sftp.get(src, target);
|
||||
sftp.get("test_file", new FileSystemFile("/tmp"));
|
||||
} finally {
|
||||
sftp.close();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@ package examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.sftp.SFTPClient;
|
||||
import net.schmizz.sshj.xfer.FileSystemFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -32,10 +33,9 @@ public class SFTPUpload {
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
final String src = System.getProperty("user.home") + File.separator + "test_file";
|
||||
final String target = "/tmp/";
|
||||
final SFTPClient sftp = ssh.newSFTPClient();
|
||||
try {
|
||||
sftp.put(src, target);
|
||||
sftp.put(new FileSystemFile(src), "/tmp");
|
||||
} finally {
|
||||
sftp.close();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -57,8 +57,8 @@ public class X11 {
|
||||
|
||||
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();
|
||||
new StreamCopier(cmd.getInputStream(), System.out).spawn("stdout");
|
||||
new StreamCopier(cmd.getErrorStream(), System.err).spawn("stderr");
|
||||
|
||||
// Wait for session & X11 channel to get closed
|
||||
ssh.getConnection().join();
|
||||
|
||||
42
src/main/java/net/schmizz/concurrent/ErrorDeliveryUtil.java
Normal file
42
src/main/java/net/schmizz/concurrent/ErrorDeliveryUtil.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* 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.concurrent;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class ErrorDeliveryUtil {
|
||||
|
||||
public static void alertPromises(Throwable x, Promise... promises) {
|
||||
for (Promise p : promises)
|
||||
p.deliverError(x);
|
||||
}
|
||||
|
||||
public static void alertPromises(Throwable x, Collection<? extends Promise> promises) {
|
||||
for (Promise p : promises)
|
||||
p.deliverError(x);
|
||||
}
|
||||
|
||||
public static void alertEvents(Throwable x, Event... events) {
|
||||
for (Event e: events)
|
||||
e.deliverError(x);
|
||||
}
|
||||
|
||||
public static void alertEvents(Throwable x, Collection<? extends Event> events) {
|
||||
for (Event e: events)
|
||||
e.deliverError(x);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,21 +18,22 @@ package net.schmizz.concurrent;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/*
|
||||
* Syntactic sugar around Future
|
||||
*/
|
||||
|
||||
/**
|
||||
* A kind of {@link Future} that caters to boolean values.
|
||||
* <p/>
|
||||
* An event can be set, cleared, or awaited, similar to Python's {@code threading.event}. The key difference is that a
|
||||
* waiter may be delivered an exception of parameterized type {@code T}. Furthermore, an event {@link #isSet()} when it
|
||||
* is not {@code null} i.e. it can be either {@code true} or {@code false} when set.
|
||||
*
|
||||
* @see Future
|
||||
* waiter may be delivered an exception of parameterized type {@code T}.
|
||||
* <p/>
|
||||
* Uses {@link Promise} under the hood.
|
||||
*/
|
||||
public class Event<T extends Throwable>
|
||||
extends Future<Boolean, T> {
|
||||
public class Event<T extends Throwable> {
|
||||
|
||||
private static final Object SOME = new Object() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SOME";
|
||||
}
|
||||
};
|
||||
|
||||
private final Promise<Object, T> promise;
|
||||
|
||||
/**
|
||||
* Creates this event with given {@code name} and exception {@code chainer}. Allocates a new {@link
|
||||
@@ -42,7 +43,7 @@ public class Event<T extends Throwable>
|
||||
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
||||
*/
|
||||
public Event(String name, ExceptionChainer<T> chainer) {
|
||||
super(name, chainer);
|
||||
promise = new Promise<Object, T>(name, chainer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,12 +54,30 @@ public class Event<T extends Throwable>
|
||||
* @param lock lock to use
|
||||
*/
|
||||
public Event(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
|
||||
super(name, chainer, lock);
|
||||
promise = new Promise<Object, T>(name, chainer, lock);
|
||||
}
|
||||
|
||||
/** Sets this event to be {@code true}. Short for {@code set(true)}. */
|
||||
public void set() {
|
||||
super.set(true);
|
||||
promise.deliver(SOME);
|
||||
}
|
||||
|
||||
/** Clear this event. A cleared event {@code !isSet()}. */
|
||||
public void clear() {
|
||||
promise.clear();
|
||||
}
|
||||
|
||||
/** Deliver the error {@code t} (after chaining) to any present or future waiters. */
|
||||
public void deliverError(Throwable t) {
|
||||
promise.deliverError(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this event is in a 'set' state. An event is set by a call to {@link #set} or {@link
|
||||
* #deliverError}
|
||||
*/
|
||||
public boolean isSet() {
|
||||
return promise.isDelivered();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +87,7 @@ public class Event<T extends Throwable>
|
||||
*/
|
||||
public void await()
|
||||
throws T {
|
||||
super.get();
|
||||
promise.retrieve();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +100,47 @@ public class Event<T extends Throwable>
|
||||
*/
|
||||
public void await(long timeout, TimeUnit unit)
|
||||
throws T {
|
||||
super.get(timeout, unit);
|
||||
promise.retrieve(timeout, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Await this event to have a definite {@code true} or {@code false} value, for {@code timeout} duration.
|
||||
* <p/>
|
||||
* If the definite value is not available when the timeout expires, returns {@code false}.
|
||||
*
|
||||
* @param timeout timeout
|
||||
* @param unit the time unit for the timeout
|
||||
*
|
||||
* @throws T if another thread meanwhile informs this event of an error
|
||||
*/
|
||||
public boolean tryAwait(long timeout, TimeUnit unit)
|
||||
throws T {
|
||||
return promise.tryRetrieve(timeout, unit) != null;
|
||||
}
|
||||
|
||||
/** @return whether there are any threads waiting on this event to be set. */
|
||||
public boolean hasWaiters() {
|
||||
return promise.hasWaiters();
|
||||
}
|
||||
|
||||
/** @return whether this event is in an error state i.e. has been delivered an error. */
|
||||
public boolean inError() {
|
||||
return promise.inError();
|
||||
}
|
||||
|
||||
/** Acquire the lock associated with this event. */
|
||||
public void lock() {
|
||||
promise.lock();
|
||||
}
|
||||
|
||||
/** Release the lock associated with this event. */
|
||||
public void unlock() {
|
||||
promise.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return promise.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,13 +24,13 @@ import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Represents future data of the parameterized type {@code V} and allows waiting on it. An exception may also be
|
||||
* Represents promised data of the parameterized type {@code V} and allows waiting on it. An exception may also be
|
||||
* delivered to a waiter, and will be of the parameterized type {@code T}.
|
||||
* <p/>
|
||||
* For atomic operations on a future, e.g. checking if a value is set and if it is not then setting it - in other words,
|
||||
* Compare-And-Set type operations - the associated lock for the future should be acquired while doing so.
|
||||
* For atomic operations on a promise, e.g. checking if a value is delivered and if it is not then setting it, the
|
||||
* associated lock for the promise should be acquired while doing so.
|
||||
*/
|
||||
public class Future<V, T extends Throwable> {
|
||||
public class Promise<V, T extends Throwable> {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@@ -43,24 +43,24 @@ public class Future<V, T extends Throwable> {
|
||||
private T pendingEx;
|
||||
|
||||
/**
|
||||
* Creates this future with given {@code name} and exception {@code chainer}. Allocates a new {@link
|
||||
* java.util.concurrent.locks.Lock lock} object for this future.
|
||||
* Creates this promise with given {@code name} and exception {@code chainer}. Allocates a new {@link
|
||||
* java.util.concurrent.locks.Lock lock} object for this promise.
|
||||
*
|
||||
* @param name name of this future
|
||||
* @param name name of this promise
|
||||
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
||||
*/
|
||||
public Future(String name, ExceptionChainer<T> chainer) {
|
||||
public Promise(String name, ExceptionChainer<T> chainer) {
|
||||
this(name, chainer, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates this future with given {@code name}, exception {@code chainer}, and associated {@code lock}.
|
||||
* Creates this promise with given {@code name}, exception {@code chainer}, and associated {@code lock}.
|
||||
*
|
||||
* @param name name of this future
|
||||
* @param name name of this promise
|
||||
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
||||
* @param lock lock to use
|
||||
*/
|
||||
public Future(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
|
||||
public Promise(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
|
||||
this.name = name;
|
||||
this.chainer = chainer;
|
||||
this.lock = lock == null ? new ReentrantLock() : lock;
|
||||
@@ -68,73 +68,94 @@ public class Future<V, T extends Throwable> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this future's value to {@code val}. Any waiters will be delivered this value.
|
||||
* Set this promise's value to {@code val}. Any waiters will be delivered this value.
|
||||
*
|
||||
* @param val the value
|
||||
*/
|
||||
public void set(V val) {
|
||||
lock();
|
||||
public void deliver(V val) {
|
||||
lock.lock();
|
||||
try {
|
||||
log.debug("Setting <<{}>> to `{}`", name, val);
|
||||
this.val = val;
|
||||
cond.signalAll();
|
||||
} finally {
|
||||
unlock();
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues error that will be thrown in any waiting thread or any thread that attempts to wait on this future
|
||||
* Queues error that will be thrown in any waiting thread or any thread that attempts to wait on this promise
|
||||
* hereafter.
|
||||
*
|
||||
* @param e the error
|
||||
*/
|
||||
public void error(Throwable e) {
|
||||
lock();
|
||||
public void deliverError(Throwable e) {
|
||||
lock.lock();
|
||||
try {
|
||||
pendingEx = chainer.chain(e);
|
||||
cond.signalAll();
|
||||
} finally {
|
||||
unlock();
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** Clears this future by setting its value and queued exception to {@code null}. */
|
||||
/** Clears this promise by setting its value and queued exception to {@code null}. */
|
||||
public void clear() {
|
||||
lock();
|
||||
lock.lock();
|
||||
try {
|
||||
pendingEx = null;
|
||||
set(null);
|
||||
deliver(null);
|
||||
} finally {
|
||||
unlock();
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait indefinitely for this future's value to be set.
|
||||
* Wait indefinitely for this promise's value to be deliver.
|
||||
*
|
||||
* @return the value
|
||||
*
|
||||
* @throws T in case another thread informs the future of an error meanwhile
|
||||
* @throws T in case another thread informs the promise of an error meanwhile
|
||||
*/
|
||||
public V get()
|
||||
public V retrieve()
|
||||
throws T {
|
||||
return get(0, TimeUnit.SECONDS);
|
||||
return tryRetrieve(0, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for {@code timeout} duration for this future's value to be set.
|
||||
* Wait for {@code timeout} duration for this promise's value to be deliver.
|
||||
*
|
||||
* @param timeout the timeout
|
||||
* @param unit time unit for the timeout
|
||||
*
|
||||
* @return the value
|
||||
*
|
||||
* @throws T in case another thread informs the future of an error meanwhile, or the timeout expires
|
||||
* @throws T in case another thread informs the promise of an error meanwhile, or the timeout expires
|
||||
*/
|
||||
public V get(long timeout, TimeUnit unit)
|
||||
public V retrieve(long timeout, TimeUnit unit)
|
||||
throws T {
|
||||
lock();
|
||||
final V value = tryRetrieve(timeout, unit);
|
||||
if (value == null)
|
||||
throw chainer.chain(new TimeoutException("Timeout expired"));
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for {@code timeout} duration for this promise's value to be deliver.
|
||||
* <p/>
|
||||
* If the value is not deliver by the time the timeout expires, returns {@code null}.
|
||||
*
|
||||
* @param timeout the timeout
|
||||
* @param unit time unit for the timeout
|
||||
*
|
||||
* @return the value or {@code null}
|
||||
*
|
||||
* @throws T in case another thread informs the promise of an error meanwhile
|
||||
*/
|
||||
public V tryRetrieve(long timeout, TimeUnit unit)
|
||||
throws T {
|
||||
lock.lock();
|
||||
try {
|
||||
if (pendingEx != null)
|
||||
throw pendingEx;
|
||||
@@ -145,7 +166,7 @@ public class Future<V, T extends Throwable> {
|
||||
if (timeout == 0)
|
||||
cond.await();
|
||||
else if (!cond.await(timeout, unit))
|
||||
throw chainer.chain(new TimeoutException("Timeout expired"));
|
||||
return null;
|
||||
if (pendingEx != null) {
|
||||
log.error("<<{}>> woke to: {}", name, pendingEx.toString());
|
||||
throw pendingEx;
|
||||
@@ -154,54 +175,53 @@ public class Future<V, T extends Throwable> {
|
||||
} catch (InterruptedException ie) {
|
||||
throw chainer.chain(ie);
|
||||
} finally {
|
||||
unlock();
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** @return whether this future has a value set, and no error waiting to pop. */
|
||||
public boolean isSet() {
|
||||
lock();
|
||||
/** @return whether this promise has a value delivered, and no error waiting to pop. */
|
||||
public boolean isDelivered() {
|
||||
lock.lock();
|
||||
try {
|
||||
return pendingEx == null && val != null;
|
||||
} finally {
|
||||
unlock();
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** @return whether this future currently has an error set. */
|
||||
public boolean hasError() {
|
||||
lock();
|
||||
/** @return whether this promise has been delivered an error. */
|
||||
public boolean inError() {
|
||||
lock.lock();
|
||||
try {
|
||||
return pendingEx != null;
|
||||
} finally {
|
||||
unlock();
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** @return whether this future has threads waiting on it. */
|
||||
/** @return whether this promise has threads waiting on it. */
|
||||
public boolean hasWaiters() {
|
||||
lock();
|
||||
lock.lock();
|
||||
try {
|
||||
return lock.hasWaiters(cond);
|
||||
} finally {
|
||||
unlock();
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock using the associated lock. Use as part of a {@code try-finally} construct in conjunction with {@link
|
||||
* #unlock()}.
|
||||
*/
|
||||
/** Acquire the lock associated with this promise. */
|
||||
public void lock() {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock using the associated lock. Use as part of a {@code try-finally} construct in conjunction with {@link
|
||||
* #lock()}.
|
||||
*/
|
||||
/** Release the lock associated with this promise. */
|
||||
public void unlock() {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -57,7 +57,7 @@ public abstract class AbstractService
|
||||
|
||||
@Override
|
||||
public void notifyError(SSHException error) {
|
||||
log.debug("Was notified of {}", error.toString());
|
||||
log.debug("Notified of {}", error.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,12 +66,6 @@ public abstract class AbstractService
|
||||
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 {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -91,7 +91,7 @@ public class DefaultConfig
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private static final String VERSION = "SSHJ_0_3";
|
||||
private static final String VERSION = "SSHJ_0_4_1";
|
||||
|
||||
public DefaultConfig() {
|
||||
setVersion(VERSION);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,6 +50,7 @@ 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.keyprovider.OpenSSHKeyFile;
|
||||
import net.schmizz.sshj.userauth.method.AuthKeyboardInteractive;
|
||||
import net.schmizz.sshj.userauth.method.AuthMethod;
|
||||
import net.schmizz.sshj.userauth.method.AuthPassword;
|
||||
@@ -64,6 +65,8 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.net.SocketAddress;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
@@ -99,8 +102,8 @@ import java.util.List;
|
||||
* client.authPassword("username", "password");
|
||||
* final Session session = client.startSession();
|
||||
* try {
|
||||
* session.exec("true");
|
||||
* client.getConnection().join();
|
||||
* final Command cmd = session.exec("true");
|
||||
* cmd.join(1, TimeUnit.SECONDS);
|
||||
* } finally {
|
||||
* session.close();
|
||||
* } finally {
|
||||
@@ -501,6 +504,33 @@ public class SSHClient
|
||||
return loadKeys(location, passphrase.toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link KeyProvider} instance from passed strings. Currently only PKCS8 format
|
||||
* private key files are supported (OpenSSH uses this format).
|
||||
* <p/>
|
||||
*
|
||||
* @param privateKey the private key as a string
|
||||
* @param publicKey the public key as a string if it's not included with the private key
|
||||
* @param passwordFinder the {@link PasswordFinder} that can supply the passphrase for decryption (may be {@code
|
||||
* null} in case keyfile is not encrypted)
|
||||
*
|
||||
* @return the key provider ready for use in authentication
|
||||
*
|
||||
* @throws SSHException if there was no suitable key provider available for the file format; typically because
|
||||
* BouncyCastle is not in the classpath
|
||||
* @throws IOException if the key file format is not known, etc.
|
||||
*/
|
||||
public KeyProvider loadKeys(String privateKey, String publicKey, PasswordFinder passwordFinder)
|
||||
throws IOException {
|
||||
final FileKeyProvider.Format format = KeyProviderUtil.detectKeyFileFormat(privateKey, publicKey != null);
|
||||
final FileKeyProvider fkp = Factory.Named.Util.create(trans.getConfig().getFileKeyProviderFactories(), format
|
||||
.toString());
|
||||
if (fkp == null)
|
||||
throw new SSHException("No provider available for " + format + " key file");
|
||||
fkp.init(privateKey, publicKey, passwordFinder);
|
||||
return fkp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts loading the user's {@code known_hosts} file from the default locations, i.e. {@code ~/.ssh/known_hosts}
|
||||
* and {@code ~/.ssh/known_hosts2} on most platforms. Adds the resulting {@link OpenSSHKnownHosts} object as a host
|
||||
@@ -657,14 +687,7 @@ public class SSHClient
|
||||
assert trans.isRunning();
|
||||
|
||||
final long start = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
trans.doKex();
|
||||
} catch (TransportException te) {
|
||||
trans.disconnect(DisconnectReason.KEY_EXCHANGE_FAILED);
|
||||
throw te;
|
||||
}
|
||||
|
||||
trans.doKex();
|
||||
log.info("Key exchange took {} seconds", (System.currentTimeMillis() - start) / 1000.0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,7 +48,4 @@ public interface Service
|
||||
void request()
|
||||
throws TransportException;
|
||||
|
||||
void notifyDisconnect()
|
||||
throws SSHException;
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -44,7 +44,8 @@ import java.util.Arrays;
|
||||
public class Buffer<T extends Buffer<T>> {
|
||||
|
||||
public static class BufferException
|
||||
extends SSHRuntimeException {
|
||||
extends SSHException {
|
||||
|
||||
public BufferException(String message) {
|
||||
super(message);
|
||||
}
|
||||
@@ -139,7 +140,8 @@ public class Buffer<T extends Buffer<T>> {
|
||||
this.wpos = wpos;
|
||||
}
|
||||
|
||||
protected void ensureAvailable(int a) {
|
||||
protected void ensureAvailable(int a)
|
||||
throws BufferException {
|
||||
if (available() < a)
|
||||
throw new BufferException("Underflow");
|
||||
}
|
||||
@@ -177,7 +179,8 @@ public class Buffer<T extends Buffer<T>> {
|
||||
*
|
||||
* @return the {@code true} or {@code false} value read
|
||||
*/
|
||||
public boolean readBoolean() {
|
||||
public boolean readBoolean()
|
||||
throws BufferException {
|
||||
return readByte() != 0;
|
||||
}
|
||||
|
||||
@@ -197,7 +200,8 @@ public class Buffer<T extends Buffer<T>> {
|
||||
*
|
||||
* @return the byte read
|
||||
*/
|
||||
public byte readByte() {
|
||||
public byte readByte()
|
||||
throws BufferException {
|
||||
ensureAvailable(1);
|
||||
return data[rpos++];
|
||||
}
|
||||
@@ -221,8 +225,9 @@ public class Buffer<T extends Buffer<T>> {
|
||||
*
|
||||
* @return the byte-array read
|
||||
*/
|
||||
public byte[] readBytes() {
|
||||
int len = readInt();
|
||||
public byte[] readBytes()
|
||||
throws BufferException {
|
||||
int len = readUInt32AsInt();
|
||||
if (len < 0 || len > 32768)
|
||||
throw new BufferException("Bad item length: " + len);
|
||||
byte[] b = new byte[len];
|
||||
@@ -251,14 +256,16 @@ public class Buffer<T extends Buffer<T>> {
|
||||
* @return this
|
||||
*/
|
||||
public T putBytes(byte[] b, int off, int len) {
|
||||
return putInt(len - off).putRawBytes(b, off, len);
|
||||
return putUInt32(len - off).putRawBytes(b, off, len);
|
||||
}
|
||||
|
||||
public void readRawBytes(byte[] buf) {
|
||||
public void readRawBytes(byte[] buf)
|
||||
throws BufferException {
|
||||
readRawBytes(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
public void readRawBytes(byte[] buf, int off, int len) {
|
||||
public void readRawBytes(byte[] buf, int off, int len)
|
||||
throws BufferException {
|
||||
ensureAvailable(len);
|
||||
System.arraycopy(data, rpos, buf, off, len);
|
||||
rpos += len;
|
||||
@@ -294,16 +301,18 @@ public class Buffer<T extends Buffer<T>> {
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public int readInt() {
|
||||
return (int) readLong();
|
||||
public int readUInt32AsInt()
|
||||
throws BufferException {
|
||||
return (int) readUInt32();
|
||||
}
|
||||
|
||||
public long readLong() {
|
||||
public long readUInt32()
|
||||
throws BufferException {
|
||||
ensureAvailable(4);
|
||||
return data[rpos++] << 24 & 0xff000000L |
|
||||
data[rpos++] << 16 & 0x00ff0000L |
|
||||
data[rpos++] << 8 & 0x0000ff00L |
|
||||
data[rpos++] & 0x000000ffL;
|
||||
data[rpos++] << 16 & 0x00ff0000L |
|
||||
data[rpos++] << 8 & 0x0000ff00L |
|
||||
data[rpos++] & 0x000000ffL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,10 +323,10 @@ public class Buffer<T extends Buffer<T>> {
|
||||
* @return this
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T putInt(long uint32) {
|
||||
public T putUInt32(long uint32) {
|
||||
ensureCapacity(4);
|
||||
if (uint32 < 0 || uint32 > 0xffffffffL)
|
||||
throw new BufferException("Invalid value: " + uint32);
|
||||
throw new RuntimeException("Invalid value: " + uint32);
|
||||
data[wpos++] = (byte) (uint32 >> 24);
|
||||
data[wpos++] = (byte) (uint32 >> 16);
|
||||
data[wpos++] = (byte) (uint32 >> 8);
|
||||
@@ -330,54 +339,29 @@ public class Buffer<T extends Buffer<T>> {
|
||||
*
|
||||
* @return the MP integer as a {@code BigInteger}
|
||||
*/
|
||||
public BigInteger readMPInt() {
|
||||
return new BigInteger(readMPIntAsBytes());
|
||||
public BigInteger readMPInt()
|
||||
throws BufferException {
|
||||
return new BigInteger(readBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an SSH multiple-precision integer from a {@code BigInteger}
|
||||
*
|
||||
* @param bi {@code BigInteger} to write
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public T putMPInt(BigInteger bi) {
|
||||
return putMPInt(bi.toByteArray());
|
||||
final byte[] asBytes = bi.toByteArray();
|
||||
putUInt32(asBytes.length);
|
||||
return putRawBytes(asBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an SSH multiple-precision integer from a Java byte-array
|
||||
*
|
||||
* @param foo byte-array
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public T putMPInt(byte[] foo) {
|
||||
int i = foo.length;
|
||||
if ((foo[0] & 0x80) != 0) {
|
||||
i++;
|
||||
putInt(i);
|
||||
putByte((byte) 0);
|
||||
} else
|
||||
putInt(i);
|
||||
return putRawBytes(foo);
|
||||
}
|
||||
|
||||
public byte[] readMPIntAsBytes() {
|
||||
return readBytes();
|
||||
}
|
||||
|
||||
public long readUINT64() {
|
||||
long uint64 = (readLong() << 32) + (readLong() & 0xffffffffL);
|
||||
public long readUInt64()
|
||||
throws BufferException {
|
||||
long uint64 = (readUInt32() << 32) + (readUInt32() & 0xffffffffL);
|
||||
if (uint64 < 0)
|
||||
throw new BufferException("Cannot handle values > Long.MAX_VALUE");
|
||||
return uint64;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T putUINT64(long uint64) {
|
||||
public T putUInt64(long uint64) {
|
||||
if (uint64 < 0)
|
||||
throw new BufferException("Invalid value: " + uint64);
|
||||
throw new RuntimeException("Invalid value: " + uint64);
|
||||
data[wpos++] = (byte) (uint64 >> 56);
|
||||
data[wpos++] = (byte) (uint64 >> 48);
|
||||
data[wpos++] = (byte) (uint64 >> 40);
|
||||
@@ -394,8 +378,9 @@ public class Buffer<T extends Buffer<T>> {
|
||||
*
|
||||
* @return the string as a Java {@code String}
|
||||
*/
|
||||
public String readString() {
|
||||
int len = readInt();
|
||||
public String readString()
|
||||
throws BufferException {
|
||||
int len = readUInt32AsInt();
|
||||
if (len < 0 || len > 32768)
|
||||
throw new BufferException("Bad item length: " + len);
|
||||
ensureAvailable(len);
|
||||
@@ -414,7 +399,8 @@ public class Buffer<T extends Buffer<T>> {
|
||||
*
|
||||
* @return the string as a byte-array
|
||||
*/
|
||||
public byte[] readStringAsBytes() {
|
||||
public byte[] readStringAsBytes()
|
||||
throws BufferException {
|
||||
return readBytes();
|
||||
}
|
||||
|
||||
@@ -427,20 +413,16 @@ public class Buffer<T extends Buffer<T>> {
|
||||
}
|
||||
|
||||
public T putString(String string) {
|
||||
try {
|
||||
return putString(string.getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
return putString(string.getBytes(IOUtils.UTF8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a char-array as an SSH string and then blanks it out.
|
||||
* <p/>
|
||||
* This is useful when a plaintext password needs to be sent. If {@code passwd} is {@code null}, an empty string is
|
||||
* This is useful when a plaintext password needs to be sent. If {@code str} is {@code null}, an empty string is
|
||||
* written.
|
||||
*
|
||||
* @param str (null-ok) the password as a character array
|
||||
* @param str (null-ok) the string as a character array
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
@@ -448,7 +430,7 @@ public class Buffer<T extends Buffer<T>> {
|
||||
public T putSensitiveString(char[] str) {
|
||||
if (str == null)
|
||||
return putString("");
|
||||
putInt(str.length);
|
||||
putUInt32(str.length);
|
||||
ensureCapacity(str.length);
|
||||
for (char c : str)
|
||||
data[wpos++] = (byte) c;
|
||||
@@ -456,7 +438,8 @@ public class Buffer<T extends Buffer<T>> {
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public PublicKey readPublicKey() {
|
||||
public PublicKey readPublicKey()
|
||||
throws BufferException {
|
||||
try {
|
||||
final String type = readString();
|
||||
return KeyType.fromString(type).readPubKeyFromBuffer(type, this);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,18 +42,6 @@ public class ByteArrayUtils {
|
||||
|
||||
final static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
/**
|
||||
* Check whether two byte arrays are the equal.
|
||||
*
|
||||
* @param a1
|
||||
* @param a2
|
||||
*
|
||||
* @return <code>true</code> or <code>false</code>
|
||||
*/
|
||||
public static boolean equals(byte[] a1, byte[] a2) {
|
||||
return (a1.length != a2.length && equals(a1, 0, a2, 0, a1.length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether some part or whole of two byte arrays is equal, for <code>length</code> bytes starting at some
|
||||
* offset.
|
||||
@@ -75,17 +63,6 @@ public class ByteArrayUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a hexadecimal representation of <code>array</code>, with each octet separated by a space.
|
||||
*
|
||||
* @param array
|
||||
*
|
||||
* @return hex string, each octet delimited by a space
|
||||
*/
|
||||
public static String printHex(byte[] array) {
|
||||
return printHex(array, 0, array.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a hexadecimal representation of a byte array starting at <code>offset</code> index for <code>len</code>
|
||||
* bytes, with each octet separated by a space.
|
||||
@@ -139,8 +116,4 @@ public class ByteArrayUtils {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static byte[] copyOf(byte[] array) {
|
||||
return Arrays.copyOf(array, array.length);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,13 +38,18 @@ package net.schmizz.sshj.common;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class IOUtils {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(IOUtils.class);
|
||||
|
||||
public static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
public static void closeQuietly(Closeable... closeables) {
|
||||
for (Closeable c : closeables)
|
||||
try {
|
||||
@@ -55,4 +60,11 @@ public class IOUtils {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public static ByteArrayOutputStream readFully(InputStream stream)
|
||||
throws IOException {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
new StreamCopier(stream, baos).copy();
|
||||
return baos;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,8 +36,13 @@ public enum KeyType {
|
||||
@Override
|
||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
||||
throws GeneralSecurityException {
|
||||
final BigInteger e = buf.readMPInt();
|
||||
final BigInteger n = buf.readMPInt();
|
||||
final BigInteger e, n;
|
||||
try {
|
||||
e = buf.readMPInt();
|
||||
n = buf.readMPInt();
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new GeneralSecurityException(be);
|
||||
}
|
||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
|
||||
return keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
||||
}
|
||||
@@ -63,10 +68,15 @@ public enum KeyType {
|
||||
@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();
|
||||
BigInteger p, q, g, y;
|
||||
try {
|
||||
p = buf.readMPInt();
|
||||
q = buf.readMPInt();
|
||||
g = buf.readMPInt();
|
||||
y = buf.readMPInt();
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new GeneralSecurityException(be);
|
||||
}
|
||||
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
|
||||
return keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -75,7 +75,8 @@ public class SSHPacket
|
||||
*
|
||||
* @return the message identifier
|
||||
*/
|
||||
public Message readMessageID() {
|
||||
public Message readMessageID()
|
||||
throws BufferException {
|
||||
return Message.fromByte(readByte());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,127 +15,143 @@
|
||||
*/
|
||||
package net.schmizz.sshj.common;
|
||||
|
||||
import net.schmizz.concurrent.Event;
|
||||
import net.schmizz.concurrent.ExceptionChainer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class StreamCopier
|
||||
extends Thread {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(StreamCopier.class);
|
||||
|
||||
public interface ErrorCallback {
|
||||
void onError(IOException ioe);
|
||||
}
|
||||
|
||||
public static ErrorCallback closeOnErrorCallback(final Closeable... toClose) {
|
||||
return new ErrorCallback() {
|
||||
@Override
|
||||
public void onError(IOException ioe) {
|
||||
IOUtils.closeQuietly(toClose);
|
||||
}
|
||||
};
|
||||
}
|
||||
public class StreamCopier {
|
||||
|
||||
public interface Listener {
|
||||
void reportProgress(long transferred);
|
||||
|
||||
void reportProgress(long transferred)
|
||||
throws IOException;
|
||||
|
||||
}
|
||||
|
||||
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];
|
||||
int read;
|
||||
while ((read = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, read);
|
||||
count += read;
|
||||
if (keepFlushing)
|
||||
out.flush();
|
||||
if (reportProgress)
|
||||
listener.reportProgress(count);
|
||||
private static final Listener NULL_LISTENER = new Listener() {
|
||||
@Override
|
||||
public void reportProgress(long transferred) {
|
||||
}
|
||||
if (!keepFlushing)
|
||||
out.flush();
|
||||
|
||||
final double sizeKiB = count / 1024.0;
|
||||
final double timeSeconds = (System.currentTimeMillis() - startTime) / 1000.0;
|
||||
LOG.info(sizeKiB + " KiB transferred in {} seconds ({} KiB/s)", timeSeconds, (sizeKiB / timeSeconds));
|
||||
|
||||
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();
|
||||
int read;
|
||||
while ((read = stream.read()) != -1)
|
||||
sb.append((char) read);
|
||||
return sb.toString();
|
||||
}
|
||||
};
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final InputStream in;
|
||||
private final OutputStream out;
|
||||
|
||||
private Listener listener = NULL_LISTENER;
|
||||
|
||||
private int bufSize = 1;
|
||||
private boolean keepFlushing = true;
|
||||
private long length = -1;
|
||||
|
||||
private ErrorCallback errCB = new ErrorCallback() {
|
||||
@Override
|
||||
public void onError(IOException ioe) {
|
||||
}
|
||||
}; // Default null cb
|
||||
|
||||
public StreamCopier(String name, InputStream in, OutputStream out) {
|
||||
public StreamCopier(InputStream in, OutputStream out) {
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
setName(name);
|
||||
}
|
||||
|
||||
public StreamCopier bufSize(int size) {
|
||||
bufSize = size;
|
||||
public StreamCopier bufSize(int bufSize) {
|
||||
this.bufSize = bufSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamCopier keepFlushing(boolean choice) {
|
||||
keepFlushing = choice;
|
||||
public StreamCopier keepFlushing(boolean keepFlushing) {
|
||||
this.keepFlushing = keepFlushing;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamCopier daemon(boolean choice) {
|
||||
setDaemon(choice);
|
||||
public StreamCopier listener(Listener listener) {
|
||||
if (listener == null) listener = NULL_LISTENER;
|
||||
this.listener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StreamCopier errorCallback(ErrorCallback errCB) {
|
||||
this.errCB = errCB;
|
||||
public StreamCopier length(long length) {
|
||||
this.length = length;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
log.debug("Wil pipe from {} to {}", in, out);
|
||||
copy(in, out, bufSize, keepFlushing);
|
||||
log.debug("EOF on {}", in);
|
||||
} catch (IOException ioe) {
|
||||
log.error("In pipe from {} to {}: " + ioe.toString(), in, out);
|
||||
errCB.onError(ioe);
|
||||
public Event<IOException> spawn(String name) {
|
||||
return spawn(name, false);
|
||||
}
|
||||
|
||||
public Event<IOException> spawnDaemon(String name) {
|
||||
return spawn(name, true);
|
||||
}
|
||||
|
||||
private Event<IOException> spawn(final String name, final boolean daemon) {
|
||||
final Event<IOException> doneEvent =
|
||||
new Event<IOException>("copyDone", new ExceptionChainer<IOException>() {
|
||||
@Override
|
||||
public IOException chain(Throwable t) {
|
||||
return (t instanceof IOException) ? (IOException) t : new IOException(t);
|
||||
}
|
||||
});
|
||||
|
||||
new Thread() {
|
||||
{
|
||||
setName(name);
|
||||
setDaemon(daemon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
log.debug("Will copy from {} to {}", in, out);
|
||||
copy();
|
||||
log.debug("Done copying from {}", in);
|
||||
doneEvent.set();
|
||||
} catch (IOException ioe) {
|
||||
log.error("In pipe from {} to {}: " + ioe.toString(), in, out);
|
||||
doneEvent.deliverError(ioe);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
return doneEvent;
|
||||
}
|
||||
|
||||
public long copy()
|
||||
throws IOException {
|
||||
final byte[] buf = new byte[bufSize];
|
||||
long count = 0;
|
||||
int read = 0;
|
||||
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
if (length == -1) {
|
||||
while ((read = in.read(buf)) != -1)
|
||||
count = write(buf, count, read);
|
||||
} else {
|
||||
while (count < length && (read = in.read(buf, 0, (int) Math.min(bufSize, length - count))) != -1)
|
||||
count = write(buf, count, read);
|
||||
}
|
||||
|
||||
if (!keepFlushing)
|
||||
out.flush();
|
||||
|
||||
final double timeSeconds = (System.currentTimeMillis() - startTime) / 1000.0;
|
||||
final double sizeKiB = count / 1024.0;
|
||||
log.info(sizeKiB + " KiB transferred in {} seconds ({} KiB/s)", timeSeconds, (sizeKiB / timeSeconds));
|
||||
|
||||
if (length != -1 && read == -1)
|
||||
throw new IOException("Encountered EOF, could not transfer " + length + " bytes");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private long write(byte[] buf, long count, int read)
|
||||
throws IOException {
|
||||
out.write(buf, 0, read);
|
||||
count += read;
|
||||
if (keepFlushing)
|
||||
out.flush();
|
||||
listener.reportProgress(count);
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.connection;
|
||||
|
||||
import net.schmizz.concurrent.Future;
|
||||
import net.schmizz.concurrent.Promise;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.connection.channel.Channel;
|
||||
import net.schmizz.sshj.connection.channel.OpenFailException;
|
||||
@@ -89,12 +89,12 @@ public interface Connection {
|
||||
* @param wantReply whether a reply is requested
|
||||
* @param specifics {@link SSHPacket} containing fields specific to the request
|
||||
*
|
||||
* @return a {@link Future} for the reply data (in case {@code wantReply} is true) which allows waiting on the
|
||||
* @return a {@link net.schmizz.concurrent.Promise} for the reply data (in case {@code wantReply} is true) which allows waiting on the
|
||||
* reply, or {@code null} if a reply is not requested.
|
||||
*
|
||||
* @throws TransportException if there is an error sending the request
|
||||
*/
|
||||
public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
|
||||
public Promise<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
|
||||
byte[] specifics)
|
||||
throws TransportException;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,22 +15,21 @@
|
||||
*/
|
||||
package net.schmizz.sshj.connection;
|
||||
|
||||
import net.schmizz.concurrent.Future;
|
||||
import net.schmizz.concurrent.FutureUtils;
|
||||
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||
import net.schmizz.concurrent.Promise;
|
||||
import net.schmizz.sshj.AbstractService;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.DisconnectReason;
|
||||
import net.schmizz.sshj.common.ErrorNotifiable;
|
||||
import net.schmizz.sshj.common.Message;
|
||||
import net.schmizz.sshj.common.SSHException;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.connection.channel.Channel;
|
||||
import net.schmizz.sshj.connection.channel.OpenFailException;
|
||||
import net.schmizz.sshj.connection.channel.OpenFailException.Reason;
|
||||
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;
|
||||
@@ -50,7 +49,7 @@ public class ConnectionImpl
|
||||
|
||||
private final Map<String, ForwardedChannelOpener> openers = new ConcurrentHashMap<String, ForwardedChannelOpener>();
|
||||
|
||||
private final Queue<Future<SSHPacket, ConnectionException>> globalReqFutures = new LinkedList<Future<SSHPacket, ConnectionException>>();
|
||||
private final Queue<Promise<SSHPacket, ConnectionException>> globalReqPromises = new LinkedList<Promise<SSHPacket, ConnectionException>>();
|
||||
|
||||
private int windowSize = 2048 * 1024;
|
||||
private int maxPacketSize = 32 * 1024;
|
||||
@@ -104,14 +103,18 @@ public class ConnectionImpl
|
||||
|
||||
private Channel getChannel(SSHPacket buffer)
|
||||
throws ConnectionException {
|
||||
int recipient = buffer.readInt();
|
||||
Channel channel = get(recipient);
|
||||
if (channel != null)
|
||||
return channel;
|
||||
else {
|
||||
buffer.rpos(buffer.rpos() - 5);
|
||||
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Received " + buffer.readMessageID()
|
||||
+ " on unknown channel #" + recipient);
|
||||
try {
|
||||
final int recipient = buffer.readUInt32AsInt();
|
||||
final Channel channel = get(recipient);
|
||||
if (channel != null)
|
||||
return channel;
|
||||
else {
|
||||
buffer.rpos(buffer.rpos() - 5);
|
||||
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
|
||||
"Received " + buffer.readMessageID() + " on unknown channel #" + recipient);
|
||||
}
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new ConnectionException(be);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,19 +143,6 @@ public class ConnectionImpl
|
||||
super.handle(msg, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyError(SSHException error) {
|
||||
super.notifyError(error);
|
||||
|
||||
synchronized (globalReqFutures) {
|
||||
FutureUtils.alertAll(error, globalReqFutures);
|
||||
globalReqFutures.clear();
|
||||
}
|
||||
|
||||
ErrorNotifiable.Util.alertAll(error, channels.values());
|
||||
channels.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPacketSize() {
|
||||
return maxPacketSize;
|
||||
@@ -193,46 +183,51 @@ public class ConnectionImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
|
||||
byte[] specifics)
|
||||
public Promise<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
|
||||
byte[] specifics)
|
||||
throws TransportException {
|
||||
synchronized (globalReqFutures) {
|
||||
synchronized (globalReqPromises) {
|
||||
log.info("Making global request for `{}`", name);
|
||||
trans.write(new SSHPacket(Message.GLOBAL_REQUEST).putString(name)
|
||||
.putBoolean(wantReply).putRawBytes(specifics));
|
||||
.putBoolean(wantReply)
|
||||
.putRawBytes(specifics));
|
||||
|
||||
Future<SSHPacket, ConnectionException> future = null;
|
||||
Promise<SSHPacket, ConnectionException> promise = null;
|
||||
if (wantReply) {
|
||||
future = new Future<SSHPacket, ConnectionException>("global req for " + name, ConnectionException.chainer);
|
||||
globalReqFutures.add(future);
|
||||
promise = new Promise<SSHPacket, ConnectionException>("global req for " + name, ConnectionException.chainer);
|
||||
globalReqPromises.add(promise);
|
||||
}
|
||||
return future;
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
private void gotGlobalReqResponse(SSHPacket response)
|
||||
throws ConnectionException {
|
||||
synchronized (globalReqFutures) {
|
||||
Future<SSHPacket, ConnectionException> gr = globalReqFutures.poll();
|
||||
synchronized (globalReqPromises) {
|
||||
Promise<SSHPacket, ConnectionException> gr = globalReqPromises.poll();
|
||||
if (gr == null)
|
||||
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
|
||||
"Got a global request response when none was requested");
|
||||
else if (response == null)
|
||||
gr.error(new ConnectionException("Global request [" + gr + "] failed"));
|
||||
gr.deliverError(new ConnectionException("Global request [" + gr + "] failed"));
|
||||
else
|
||||
gr.set(response);
|
||||
gr.deliver(response);
|
||||
}
|
||||
}
|
||||
|
||||
private void gotChannelOpen(SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
final String type = buf.readString();
|
||||
log.debug("Received CHANNEL_OPEN for `{}` channel", type);
|
||||
if (openers.containsKey(type))
|
||||
openers.get(type).handleOpen(buf);
|
||||
else {
|
||||
log.warn("No opener found for `{}` CHANNEL_OPEN request -- rejecting", type);
|
||||
sendOpenFailure(buf.readInt(), OpenFailException.Reason.UNKNOWN_CHANNEL_TYPE, "");
|
||||
try {
|
||||
final String type = buf.readString();
|
||||
log.debug("Received CHANNEL_OPEN for `{}` channel", type);
|
||||
if (openers.containsKey(type))
|
||||
openers.get(type).handleOpen(buf);
|
||||
else {
|
||||
log.warn("No opener found for `{}` CHANNEL_OPEN request -- rejecting", type);
|
||||
sendOpenFailure(buf.readUInt32AsInt(), Reason.UNKNOWN_CHANNEL_TYPE, "");
|
||||
}
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new ConnectionException(be);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,18 +235,20 @@ public class ConnectionImpl
|
||||
public void sendOpenFailure(int recipient, Reason reason, String message)
|
||||
throws TransportException {
|
||||
trans.write(new SSHPacket(Message.CHANNEL_OPEN_FAILURE)
|
||||
.putInt(recipient)
|
||||
.putInt(reason.getCode())
|
||||
.putString(message));
|
||||
.putUInt32(recipient)
|
||||
.putUInt32(reason.getCode())
|
||||
.putString(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDisconnect()
|
||||
throws SSHException {
|
||||
super.notifyDisconnect();
|
||||
final ConnectionException ex = new ConnectionException("Disconnected.");
|
||||
FutureUtils.alertAll(ex, globalReqFutures);
|
||||
ErrorNotifiable.Util.alertAll(ex, new HashSet<Channel>(channels.values()));
|
||||
public void notifyError(SSHException error) {
|
||||
super.notifyError(error);
|
||||
synchronized (globalReqPromises) {
|
||||
ErrorDeliveryUtil.alertPromises(error, globalReqPromises);
|
||||
globalReqPromises.clear();
|
||||
}
|
||||
ErrorNotifiable.Util.alertAll(error, channels.values());
|
||||
channels.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,7 +36,7 @@
|
||||
package net.schmizz.sshj.connection.channel;
|
||||
|
||||
import net.schmizz.concurrent.Event;
|
||||
import net.schmizz.concurrent.FutureUtils;
|
||||
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.ByteArrayUtils;
|
||||
import net.schmizz.sshj.common.DisconnectReason;
|
||||
@@ -183,11 +183,11 @@ public abstract class AbstractChannel
|
||||
break;
|
||||
|
||||
case CHANNEL_EXTENDED_DATA:
|
||||
gotExtendedData(buf.readInt(), buf);
|
||||
gotExtendedData(buf);
|
||||
break;
|
||||
|
||||
case CHANNEL_WINDOW_ADJUST:
|
||||
gotWindowAdjustment(buf.readInt());
|
||||
gotWindowAdjustment(buf);
|
||||
break;
|
||||
|
||||
case CHANNEL_REQUEST:
|
||||
@@ -236,8 +236,8 @@ public abstract class AbstractChannel
|
||||
public void notifyError(SSHException error) {
|
||||
log.debug("Channel #{} got notified of {}", getID(), error.toString());
|
||||
|
||||
FutureUtils.alertAll(error, open, close);
|
||||
FutureUtils.alertAll(error, chanReqResponseEvents);
|
||||
ErrorDeliveryUtil.alertEvents(error, open, close);
|
||||
ErrorDeliveryUtil.alertEvents(error, chanReqResponseEvents);
|
||||
|
||||
in.notifyError(error);
|
||||
out.notifyError(error);
|
||||
@@ -258,7 +258,7 @@ public abstract class AbstractChannel
|
||||
try {
|
||||
sendClose();
|
||||
} catch (TransportException e) {
|
||||
if (!close.hasError())
|
||||
if (!close.inError())
|
||||
throw e;
|
||||
}
|
||||
close.await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
@@ -301,13 +301,24 @@ public abstract class AbstractChannel
|
||||
|
||||
private void gotChannelRequest(SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
final String reqType = buf.readString();
|
||||
buf.readBoolean(); // We don't care about the 'want-reply' value
|
||||
final String reqType;
|
||||
try {
|
||||
reqType = buf.readString();
|
||||
buf.readBoolean(); // We don't care about the 'want-reply' value
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new ConnectionException(be);
|
||||
}
|
||||
log.info("Got chan request for `{}`", reqType);
|
||||
handleRequest(reqType, buf);
|
||||
}
|
||||
|
||||
private void gotWindowAdjustment(int howMuch) {
|
||||
private void gotWindowAdjustment(SSHPacket buf) throws ConnectionException {
|
||||
final int howMuch;
|
||||
try {
|
||||
howMuch = buf.readUInt32AsInt();
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new ConnectionException(be);
|
||||
}
|
||||
log.info("Received window adjustment for {} bytes", howMuch);
|
||||
rwin.expand(howMuch);
|
||||
}
|
||||
@@ -317,10 +328,10 @@ public abstract class AbstractChannel
|
||||
close.set();
|
||||
}
|
||||
|
||||
protected void gotExtendedData(int dataTypeCode, SSHPacket buf)
|
||||
protected void gotExtendedData(SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Extended data not supported on " + type
|
||||
+ " channel");
|
||||
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
|
||||
"Extended data not supported on " + type + " channel");
|
||||
}
|
||||
|
||||
protected void gotUnknown(Message msg, SSHPacket buf)
|
||||
@@ -333,12 +344,17 @@ public abstract class AbstractChannel
|
||||
}
|
||||
|
||||
protected SSHPacket newBuffer(Message cmd) {
|
||||
return new SSHPacket(cmd).putInt(recipient);
|
||||
return new SSHPacket(cmd).putUInt32(recipient);
|
||||
}
|
||||
|
||||
protected void receiveInto(ChannelInputStream stream, SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
final int len = buf.readInt();
|
||||
final int len;
|
||||
try {
|
||||
len = buf.readUInt32AsInt();
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new ConnectionException(be);
|
||||
}
|
||||
if (len < 0 || len > getLocalMaxPacketSize() || len > buf.available())
|
||||
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Bad item length: " + len);
|
||||
if (log.isTraceEnabled())
|
||||
@@ -372,7 +388,7 @@ public abstract class AbstractChannel
|
||||
if (success)
|
||||
responseEvent.set();
|
||||
else
|
||||
responseEvent.error(new ConnectionException("Request failed"));
|
||||
responseEvent.deliverError(new ConnectionException("Request failed"));
|
||||
} else
|
||||
throw new ConnectionException(
|
||||
DisconnectReason.PROTOCOL_ERROR,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -163,7 +163,7 @@ public final class ChannelInputStream
|
||||
if (adjustment > 0) {
|
||||
log.info("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST to #{} for {} bytes", chan.getRecipient(), adjustment);
|
||||
trans.write(new SSHPacket(Message.CHANNEL_WINDOW_ADJUST)
|
||||
.putInt(chan.getRecipient()).putInt(adjustment));
|
||||
.putUInt32(chan.getRecipient()).putUInt32(adjustment));
|
||||
win.expand(adjustment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -74,8 +74,8 @@ public final class ChannelOutputStream
|
||||
buffer.rpos(5);
|
||||
buffer.wpos(5);
|
||||
buffer.putMessageID(Message.CHANNEL_DATA);
|
||||
buffer.putInt(0); // meant to be recipient
|
||||
buffer.putInt(0); // meant to be data length
|
||||
buffer.putUInt32(0); // meant to be recipient
|
||||
buffer.putUInt32(0); // meant to be data length
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -153,8 +153,8 @@ public final class ChannelOutputStream
|
||||
private void putRecipientAndLength() {
|
||||
final int origPos = buffer.wpos();
|
||||
buffer.wpos(6);
|
||||
buffer.putInt(chan.getRecipient());
|
||||
buffer.putInt(bufferLength);
|
||||
buffer.putUInt32(chan.getRecipient());
|
||||
buffer.putUInt32(bufferLength);
|
||||
buffer.wpos(origPos);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* 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.connection.channel;
|
||||
|
||||
import net.schmizz.concurrent.Event;
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SocketStreamCopyMonitor
|
||||
extends Thread {
|
||||
|
||||
private SocketStreamCopyMonitor(Runnable r) {
|
||||
super(r);
|
||||
setName("sockmon");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
private static Closeable wrapSocket(final Socket socket) {
|
||||
return new Closeable() {
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException {
|
||||
socket.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void monitor(final int frequency, final TimeUnit unit,
|
||||
final Event<IOException> x, final Event<IOException> y,
|
||||
final Channel channel, final Socket socket) {
|
||||
new SocketStreamCopyMonitor(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
for (Event<IOException> ev = x;
|
||||
!ev.tryAwait(frequency, unit);
|
||||
ev = (ev == x) ? y : x) {
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
} finally {
|
||||
IOUtils.closeQuietly(channel, wrapSocket(socket));
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,6 +35,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.connection.channel.direct;
|
||||
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.Message;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.connection.Connection;
|
||||
@@ -67,22 +68,32 @@ public abstract class AbstractDirectChannel
|
||||
open.await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void gotOpenConfirmation(SSHPacket buf) {
|
||||
init(buf.readInt(), buf.readInt(), buf.readInt());
|
||||
private void gotOpenConfirmation(SSHPacket buf)
|
||||
throws ConnectionException {
|
||||
try {
|
||||
init(buf.readUInt32AsInt(), buf.readUInt32AsInt(), buf.readUInt32AsInt());
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new ConnectionException(be);
|
||||
}
|
||||
open.set();
|
||||
}
|
||||
|
||||
private void gotOpenFailure(SSHPacket buf) {
|
||||
open.error(new OpenFailException(getType(), buf.readInt(), buf.readString()));
|
||||
private void gotOpenFailure(SSHPacket buf)
|
||||
throws ConnectionException {
|
||||
try {
|
||||
open.deliverError(new OpenFailException(getType(), buf.readUInt32AsInt(), buf.readString()));
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new ConnectionException(be);
|
||||
}
|
||||
finishOff();
|
||||
}
|
||||
|
||||
protected SSHPacket buildOpenReq() {
|
||||
return new SSHPacket(Message.CHANNEL_OPEN)
|
||||
.putString(getType())
|
||||
.putInt(getID())
|
||||
.putInt(getLocalWinSize())
|
||||
.putInt(getLocalMaxPacketSize());
|
||||
.putUInt32(getID())
|
||||
.putUInt32(getLocalWinSize())
|
||||
.putUInt32(getLocalMaxPacketSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,19 +15,20 @@
|
||||
*/
|
||||
package net.schmizz.sshj.connection.channel.direct;
|
||||
|
||||
import net.schmizz.concurrent.Event;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.common.StreamCopier;
|
||||
import net.schmizz.sshj.common.StreamCopier.ErrorCallback;
|
||||
import net.schmizz.sshj.connection.Connection;
|
||||
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.net.ServerSocketFactory;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class LocalPortForwarder {
|
||||
|
||||
@@ -45,36 +46,22 @@ public class LocalPortForwarder {
|
||||
throws IOException {
|
||||
sock.setSendBufferSize(getLocalMaxPacketSize());
|
||||
sock.setReceiveBufferSize(getRemoteMaxPacketSize());
|
||||
|
||||
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)
|
||||
.start();
|
||||
|
||||
new StreamCopier("soc2chan", sock.getInputStream(), getOutputStream())
|
||||
final Event<IOException> soc2chan = new StreamCopier(sock.getInputStream(), getOutputStream())
|
||||
.bufSize(getRemoteMaxPacketSize())
|
||||
.errorCallback(closer)
|
||||
.daemon(true)
|
||||
.start();
|
||||
}
|
||||
.spawnDaemon("soc2chan");
|
||||
final Event<IOException> chan2soc = new StreamCopier(getInputStream(), sock.getOutputStream())
|
||||
.bufSize(getLocalMaxPacketSize())
|
||||
.spawnDaemon("chan2soc");
|
||||
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, sock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SSHPacket buildOpenReq() {
|
||||
return super.buildOpenReq()
|
||||
.putString(host)
|
||||
.putInt(port)
|
||||
.putString(ss.getInetAddress().getHostAddress())
|
||||
.putInt(ss.getLocalPort());
|
||||
.putString(host)
|
||||
.putUInt32(port)
|
||||
.putString(ss.getInetAddress().getHostAddress())
|
||||
.putUInt32(ss.getLocalPort());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -148,6 +135,7 @@ public class LocalPortForwarder {
|
||||
chan.open();
|
||||
chan.start();
|
||||
}
|
||||
log.info("Interrupted!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -140,7 +140,7 @@ public enum PTYMode {
|
||||
Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
|
||||
for (Entry<PTYMode, Integer> entry : modes.entrySet()) {
|
||||
buf.putByte(entry.getKey().getOpcode());
|
||||
buf.putInt(entry.getValue());
|
||||
buf.putUInt32(entry.getValue());
|
||||
}
|
||||
buf.putByte((byte) 0);
|
||||
return buf.getCompactData();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -43,16 +43,6 @@ public interface Session
|
||||
interface Command
|
||||
extends Channel {
|
||||
|
||||
/**
|
||||
* Read from the command's {@code stderr} stream into a string (blocking).
|
||||
*
|
||||
* @return the commands {@code stderr} output as a string
|
||||
*
|
||||
* @throws IOException if error reading from the stream
|
||||
*/
|
||||
String getErrorAsString()
|
||||
throws IOException;
|
||||
|
||||
/** Returns the command's {@code stderr} stream. */
|
||||
InputStream getErrorStream();
|
||||
|
||||
@@ -81,16 +71,6 @@ public interface Session
|
||||
*/
|
||||
Boolean getExitWasCoreDumped();
|
||||
|
||||
/**
|
||||
* Read from the command's {@code stdout} stream into a string (blocking).
|
||||
*
|
||||
* @return the command's {@code stdout} output as a string
|
||||
*
|
||||
* @throws IOException if error reading from the stream
|
||||
*/
|
||||
String getOutputAsString()
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Send a signal to the remote command.
|
||||
*
|
||||
@@ -101,6 +81,12 @@ public interface Session
|
||||
void signal(Signal signal)
|
||||
throws TransportException;
|
||||
|
||||
@Deprecated
|
||||
String getOutputAsString() throws IOException;
|
||||
|
||||
@Deprecated
|
||||
String getErrorAsString() throws IOException;
|
||||
|
||||
}
|
||||
|
||||
/** Shell API. */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,7 +35,12 @@
|
||||
*/
|
||||
package net.schmizz.sshj.connection.channel.direct;
|
||||
|
||||
import net.schmizz.sshj.common.*;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.DisconnectReason;
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
import net.schmizz.sshj.common.SSHException;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||
import net.schmizz.sshj.connection.Connection;
|
||||
import net.schmizz.sshj.connection.ConnectionException;
|
||||
import net.schmizz.sshj.connection.channel.ChannelInputStream;
|
||||
@@ -47,9 +52,7 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* {@link Session} implementation.
|
||||
*/
|
||||
/** {@link Session} implementation. */
|
||||
public class SessionChannel
|
||||
extends AbstractDirectChannel
|
||||
implements Session, Session.Command, Session.Shell, Session.Subsystem {
|
||||
@@ -84,10 +87,10 @@ public class SessionChannel
|
||||
true,
|
||||
new Buffer.PlainBuffer()
|
||||
.putString(term)
|
||||
.putInt(cols)
|
||||
.putInt(rows)
|
||||
.putInt(width)
|
||||
.putInt(height)
|
||||
.putUInt32(cols)
|
||||
.putUInt32(rows)
|
||||
.putUInt32(width)
|
||||
.putUInt32(height)
|
||||
.putBytes(PTYMode.encode(modes))
|
||||
).await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
}
|
||||
@@ -104,10 +107,10 @@ public class SessionChannel
|
||||
"pty-req",
|
||||
false,
|
||||
new Buffer.PlainBuffer()
|
||||
.putInt(cols)
|
||||
.putInt(rows)
|
||||
.putInt(width)
|
||||
.putInt(height)
|
||||
.putUInt32(cols)
|
||||
.putUInt32(rows)
|
||||
.putUInt32(width)
|
||||
.putUInt32(height)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,12 +125,6 @@ public class SessionChannel
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorAsString()
|
||||
throws IOException {
|
||||
return StreamCopier.copyStreamToString(err);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getErrorStream() {
|
||||
return err;
|
||||
@@ -148,26 +145,24 @@ public class SessionChannel
|
||||
return exitStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOutputAsString()
|
||||
throws IOException {
|
||||
return StreamCopier.copyStreamToString(getInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(String req, SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
if ("xon-xoff".equals(req))
|
||||
canDoFlowControl = buf.readBoolean();
|
||||
else if ("exit-status".equals(req))
|
||||
exitStatus = buf.readInt();
|
||||
else if ("exit-signal".equals(req)) {
|
||||
exitSignal = Signal.fromString(buf.readString());
|
||||
wasCoreDumped = buf.readBoolean(); // core dumped
|
||||
exitErrMsg = buf.readString();
|
||||
sendClose();
|
||||
} else
|
||||
super.handleRequest(req, buf);
|
||||
try {
|
||||
if ("xon-xoff".equals(req))
|
||||
canDoFlowControl = buf.readBoolean();
|
||||
else if ("exit-status".equals(req))
|
||||
exitStatus = buf.readUInt32AsInt();
|
||||
else if ("exit-signal".equals(req)) {
|
||||
exitSignal = Signal.fromString(buf.readString());
|
||||
wasCoreDumped = buf.readBoolean(); // core dumped
|
||||
exitErrMsg = buf.readString();
|
||||
sendClose();
|
||||
} else
|
||||
super.handleRequest(req, buf);
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new ConnectionException(be);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -180,7 +175,7 @@ public class SessionChannel
|
||||
.putBoolean(false)
|
||||
.putString(authProto)
|
||||
.putString(authCookie)
|
||||
.putInt(screen)
|
||||
.putUInt32(screen)
|
||||
).await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@@ -235,12 +230,18 @@ public class SessionChannel
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void gotExtendedData(int dataTypeCode, SSHPacket buf)
|
||||
protected void gotExtendedData(SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
if (dataTypeCode == 1)
|
||||
receiveInto(err, buf);
|
||||
else
|
||||
super.gotExtendedData(dataTypeCode, buf);
|
||||
try {
|
||||
final int dataTypeCode = buf.readUInt32AsInt();
|
||||
if (dataTypeCode == 1)
|
||||
receiveInto(err, buf);
|
||||
else
|
||||
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
|
||||
"Bad extended data type = " + dataTypeCode);
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new ConnectionException(be);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -254,4 +255,18 @@ public class SessionChannel
|
||||
throw new SSHRuntimeException("This session channel is all used up");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public String getOutputAsString()
|
||||
throws IOException {
|
||||
return IOUtils.readFully(getInputStream()).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public String getErrorAsString()
|
||||
throws IOException {
|
||||
return IOUtils.readFully(getErrorStream()).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -69,9 +69,9 @@ public abstract class AbstractForwardedChannel
|
||||
// Must ensure channel is attached before confirming, data could start coming in immediately!
|
||||
conn.attach(this);
|
||||
trans.write(newBuffer(Message.CHANNEL_OPEN_CONFIRMATION)
|
||||
.putInt(getID())
|
||||
.putInt(getLocalWinSize())
|
||||
.putInt(getLocalMaxPacketSize()));
|
||||
.putUInt32(getID())
|
||||
.putUInt32(getLocalWinSize())
|
||||
.putUInt32(getLocalMaxPacketSize()));
|
||||
open.set();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,7 +50,7 @@ public abstract class AbstractForwardedChannelOpener
|
||||
new Thread() {
|
||||
|
||||
{
|
||||
setName("ConnectListener");
|
||||
setName("chanopener");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -168,7 +168,11 @@ public class RemotePortForwarder
|
||||
throws ConnectionException, TransportException {
|
||||
SSHPacket reply = req(PF_REQ, forward);
|
||||
if (forward.port == 0)
|
||||
forward.port = reply.readInt();
|
||||
try {
|
||||
forward.port = reply.readUInt32AsInt();
|
||||
} catch (Buffer.BufferException e) {
|
||||
throw new ConnectionException(e);
|
||||
}
|
||||
log.info("Remote end listening on {}", forward);
|
||||
listeners.put(forward, listener);
|
||||
return forward;
|
||||
@@ -193,10 +197,10 @@ public class RemotePortForwarder
|
||||
|
||||
protected SSHPacket req(String reqName, Forward forward)
|
||||
throws ConnectionException, TransportException {
|
||||
final byte[] specifics = new Buffer.PlainBuffer().putString(forward.address).putInt(forward.port)
|
||||
final byte[] specifics = new Buffer.PlainBuffer().putString(forward.address).putUInt32(forward.port)
|
||||
.getCompactData();
|
||||
return conn.sendGlobalRequest(reqName, true, specifics)
|
||||
.get(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
.retrieve(conn.getTimeout(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/** @return the active forwards. */
|
||||
@@ -211,9 +215,14 @@ public class RemotePortForwarder
|
||||
@Override
|
||||
public void handleOpen(SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
final ForwardedTCPIPChannel chan = new ForwardedTCPIPChannel(conn, buf.readInt(), buf.readInt(), buf.readInt(),
|
||||
new Forward(buf.readString(), buf.readInt()),
|
||||
buf.readString(), buf.readInt());
|
||||
final ForwardedTCPIPChannel chan;
|
||||
try {
|
||||
chan = new ForwardedTCPIPChannel(conn, buf.readUInt32AsInt(), buf.readUInt32AsInt(), buf.readUInt32AsInt(),
|
||||
new Forward(buf.readString(), buf.readUInt32AsInt()),
|
||||
buf.readString(), buf.readUInt32AsInt());
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new ConnectionException(be);
|
||||
}
|
||||
if (listeners.containsKey(chan.getParentForward()))
|
||||
callListener(listeners.get(chan.getParentForward()), chan);
|
||||
else
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,16 +15,17 @@
|
||||
*/
|
||||
package net.schmizz.sshj.connection.channel.forwarded;
|
||||
|
||||
import net.schmizz.concurrent.Event;
|
||||
import net.schmizz.sshj.common.StreamCopier;
|
||||
import net.schmizz.sshj.common.StreamCopier.ErrorCallback;
|
||||
import net.schmizz.sshj.connection.channel.Channel;
|
||||
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** A {@link ConnectListener} that forwards what is received over the channel to a socket and vice-versa. */
|
||||
public class SocketForwardingConnectListener
|
||||
@@ -54,25 +55,15 @@ public class SocketForwardingConnectListener
|
||||
// ok so far -- could connect, let's confirm the channel
|
||||
chan.confirm();
|
||||
|
||||
final ErrorCallback closer = StreamCopier.closeOnErrorCallback(chan, new Closeable() {
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException {
|
||||
sock.close();
|
||||
}
|
||||
});
|
||||
|
||||
new StreamCopier("soc2chan", sock.getInputStream(), chan.getOutputStream())
|
||||
final Event<IOException> soc2chan = new StreamCopier(sock.getInputStream(), chan.getOutputStream())
|
||||
.bufSize(chan.getRemoteMaxPacketSize())
|
||||
.errorCallback(closer)
|
||||
.daemon(true)
|
||||
.start();
|
||||
.spawnDaemon("soc2chan");
|
||||
|
||||
new StreamCopier("chan2soc", chan.getInputStream(), sock.getOutputStream())
|
||||
final Event<IOException> chan2soc = new StreamCopier(chan.getInputStream(), sock.getOutputStream())
|
||||
.bufSize(chan.getLocalMaxPacketSize())
|
||||
.errorCallback(closer)
|
||||
.daemon(true)
|
||||
.start();
|
||||
.spawnDaemon("chan2soc");
|
||||
|
||||
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, chan2soc, soc2chan, chan, sock);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.connection.channel.forwarded;
|
||||
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.connection.Connection;
|
||||
import net.schmizz.sshj.connection.ConnectionException;
|
||||
@@ -55,10 +56,14 @@ public class X11Forwarder
|
||||
@Override
|
||||
public void handleOpen(SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
callListener(listener, new X11Channel(conn,
|
||||
buf.readInt(),
|
||||
buf.readInt(), buf.readInt(),
|
||||
buf.readString(), buf.readInt()));
|
||||
try {
|
||||
callListener(listener, new X11Channel(conn,
|
||||
buf.readUInt32AsInt(),
|
||||
buf.readUInt32AsInt(), buf.readUInt32AsInt(),
|
||||
buf.readString(), buf.readUInt32AsInt()));
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new ConnectionException(be);
|
||||
}
|
||||
}
|
||||
|
||||
/** Stop handling {@code x11} channel open requests. De-registers itself with connection layer. */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -119,26 +119,26 @@ public final class FileAttributes {
|
||||
|
||||
public byte[] toBytes() {
|
||||
Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
|
||||
buf.putInt(mask);
|
||||
buf.putUInt32(mask);
|
||||
|
||||
if (has(Flag.SIZE))
|
||||
buf.putUINT64(size);
|
||||
buf.putUInt64(size);
|
||||
|
||||
if (has(Flag.UIDGID)) {
|
||||
buf.putInt(uid);
|
||||
buf.putInt(gid);
|
||||
buf.putUInt32(uid);
|
||||
buf.putUInt32(gid);
|
||||
}
|
||||
|
||||
if (has(Flag.MODE))
|
||||
buf.putInt(mode.getMask());
|
||||
buf.putUInt32(mode.getMask());
|
||||
|
||||
if (has(Flag.ACMODTIME)) {
|
||||
buf.putInt(atime);
|
||||
buf.putInt(mtime);
|
||||
buf.putUInt32(atime);
|
||||
buf.putUInt32(mtime);
|
||||
}
|
||||
|
||||
if (has(Flag.EXTENDED)) {
|
||||
buf.putInt(ext.size());
|
||||
buf.putUInt32(ext.size());
|
||||
for (Entry<String, String> entry : ext.entrySet()) {
|
||||
buf.putString(entry.getKey());
|
||||
buf.putString(entry.getValue());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.sftp;
|
||||
|
||||
import net.schmizz.concurrent.Future;
|
||||
import net.schmizz.concurrent.Promise;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -31,7 +31,7 @@ public class PacketReader
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final InputStream in;
|
||||
private final Map<Long, Future<Response, SFTPException>> futures = new ConcurrentHashMap<Long, Future<Response, SFTPException>>();
|
||||
private final Map<Long, Promise<Response, SFTPException>> promises = new ConcurrentHashMap<Long, Promise<Response, SFTPException>>();
|
||||
private final SFTPPacket<Response> packet = new SFTPPacket<Response>();
|
||||
private final byte[] lenBuf = new byte[4];
|
||||
private final SFTPEngine engine;
|
||||
@@ -85,25 +85,25 @@ public class PacketReader
|
||||
handle();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
for (Future<Response, SFTPException> future : futures.values())
|
||||
future.error(e);
|
||||
for (Promise<Response, SFTPException> promise : promises.values())
|
||||
promise.deliverError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void handle()
|
||||
throws SFTPException {
|
||||
Response resp = new Response(packet, engine.getOperativeProtocolVersion());
|
||||
Future<Response, SFTPException> future = futures.remove(resp.getRequestID());
|
||||
Promise<Response, SFTPException> promise = promises.remove(resp.getRequestID());
|
||||
log.debug("Received {} packet", resp.getType());
|
||||
if (future == null)
|
||||
if (promise == null)
|
||||
throw new SFTPException("Received [" + resp.readType() + "] response for request-id " + resp.getRequestID()
|
||||
+ ", no such request was made");
|
||||
else
|
||||
future.set(resp);
|
||||
promise.deliver(resp);
|
||||
}
|
||||
|
||||
public void expectResponseTo(Request req) {
|
||||
futures.put(req.getRequestID(), req.getResponseFuture());
|
||||
promises.put(req.getRequestID(), req.getResponsePromise());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,23 +17,23 @@ package net.schmizz.sshj.sftp;
|
||||
|
||||
public class PathComponents {
|
||||
|
||||
public static String adjustForParent(String parent, String path) {
|
||||
return (path.startsWith("/")) ? path // Absolute path, nothing to adjust
|
||||
: (parent + (parent.endsWith("/") ? "" : "/") + path); // Relative path
|
||||
static String adjustForParent(String parent, String path, String pathSep) {
|
||||
return (path.startsWith(pathSep)) ? path // Absolute path, nothing to adjust
|
||||
: (parent + (parent.endsWith(pathSep) ? "" : pathSep) + path); // Relative path
|
||||
}
|
||||
|
||||
private static String trimFinalSlash(String path) {
|
||||
return path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
|
||||
static String trimTrailingSeparator(String somePath, String pathSep) {
|
||||
return somePath.endsWith(pathSep) ? somePath.substring(0, somePath.length() - pathSep.length()) : somePath;
|
||||
}
|
||||
|
||||
private final String parent;
|
||||
private final String name;
|
||||
private final String path;
|
||||
|
||||
public PathComponents(String parent, String name) {
|
||||
public PathComponents(String parent, String name, String pathSep) {
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
this.path = adjustForParent(parent, name);
|
||||
this.path = trimTrailingSeparator(adjustForParent(parent, name, pathSep), pathSep);
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
@@ -50,17 +50,12 @@ public class PathComponents {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof PathComponents) {
|
||||
final PathComponents that = (PathComponents) o;
|
||||
return (trimFinalSlash(path).equals(trimFinalSlash(that.path)));
|
||||
}
|
||||
|
||||
return false;
|
||||
return this == o || ((o instanceof PathComponents) && path.equals(((PathComponents) o).path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return trimFinalSlash(path).hashCode();
|
||||
return path.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,11 +19,32 @@ import java.io.IOException;
|
||||
|
||||
public class PathHelper {
|
||||
|
||||
public static final String DEFAULT_PATH_SEPARATOR = "/";
|
||||
|
||||
private final SFTPEngine engine;
|
||||
private final String pathSep;
|
||||
|
||||
private String dotDir;
|
||||
|
||||
public PathHelper(SFTPEngine engine) {
|
||||
public PathHelper(SFTPEngine engine, String pathSep) {
|
||||
this.engine = engine;
|
||||
this.pathSep = pathSep;
|
||||
}
|
||||
|
||||
public String adjustForParent(String parent, String path) {
|
||||
return PathComponents.adjustForParent(parent, path, pathSep);
|
||||
}
|
||||
|
||||
public String trimTrailingSeparator(String path) {
|
||||
return PathComponents.trimTrailingSeparator(path, pathSep);
|
||||
}
|
||||
|
||||
public String getPathSeparator() {
|
||||
return pathSep;
|
||||
}
|
||||
|
||||
public PathComponents getComponents(String parent, String name) {
|
||||
return new PathComponents(parent, name, pathSep);
|
||||
}
|
||||
|
||||
public PathComponents getComponents(String path)
|
||||
@@ -31,21 +52,21 @@ public class PathHelper {
|
||||
if (path.isEmpty() || path.equals("."))
|
||||
return getComponents(getDotDir());
|
||||
|
||||
final int lastSlash = path.lastIndexOf("/");
|
||||
final int lastSlash = path.lastIndexOf(pathSep);
|
||||
|
||||
if (lastSlash == -1)
|
||||
if (lastSlash == -1) // Relative path
|
||||
if (path.equals(".."))
|
||||
return getComponents(canon(path));
|
||||
else
|
||||
return new PathComponents(getDotDir(), path);
|
||||
return getComponents(getDotDir(), path);
|
||||
|
||||
final String name = path.substring(lastSlash + 1);
|
||||
final String name = path.substring(lastSlash + pathSep.length());
|
||||
|
||||
if (name.equals(".") || name.equals(".."))
|
||||
return getComponents(canon(path));
|
||||
else {
|
||||
final String parent = path.substring(0, lastSlash);
|
||||
return new PathComponents(parent, name);
|
||||
return getComponents(parent, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,12 +37,13 @@ public class RemoteDirectory
|
||||
switch (res.getType()) {
|
||||
|
||||
case NAME:
|
||||
final int count = res.readInt();
|
||||
final int count = res.readUInt32AsInt();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final String name = res.readString();
|
||||
res.readString(); // long name - IGNORED - shdve never been in the protocol
|
||||
final FileAttributes attrs = res.readFileAttributes();
|
||||
RemoteResourceInfo inf = new RemoteResourceInfo(path, name, attrs);
|
||||
final PathComponents comps = requester.getPathHelper().getComponents(path, name);
|
||||
final RemoteResourceInfo inf = new RemoteResourceInfo(comps, attrs);
|
||||
if (!(name.equals(".") || name.equals("..")) && (filter == null || filter.accept(inf)))
|
||||
rri.add(inf);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -55,10 +55,10 @@ public class RemoteFile
|
||||
|
||||
public int read(long fileOffset, byte[] to, int offset, int len)
|
||||
throws IOException {
|
||||
Response res = requester.doRequest(newRequest(PacketType.READ).putUINT64(fileOffset).putInt(len));
|
||||
Response res = requester.doRequest(newRequest(PacketType.READ).putUInt64(fileOffset).putUInt32(len));
|
||||
switch (res.getType()) {
|
||||
case DATA:
|
||||
int recvLen = res.readInt();
|
||||
int recvLen = res.readUInt32AsInt();
|
||||
System.arraycopy(res.array(), res.rpos(), to, offset, recvLen);
|
||||
return recvLen;
|
||||
|
||||
@@ -74,8 +74,8 @@ 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)
|
||||
.putUInt64(fileOffset)
|
||||
.putUInt32(len - off)
|
||||
.putRawBytes(data, off, len)
|
||||
).ensureStatusPacketIsOK();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,10 +20,6 @@ public class RemoteResourceInfo {
|
||||
private final PathComponents comps;
|
||||
private final FileAttributes attrs;
|
||||
|
||||
public RemoteResourceInfo(String parent, String name, FileAttributes attrs) {
|
||||
this(new PathComponents(parent, name), attrs);
|
||||
}
|
||||
|
||||
public RemoteResourceInfo(PathComponents comps, FileAttributes attrs) {
|
||||
this.comps = comps;
|
||||
this.attrs = attrs;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,21 +15,21 @@
|
||||
*/
|
||||
package net.schmizz.sshj.sftp;
|
||||
|
||||
import net.schmizz.concurrent.Future;
|
||||
import net.schmizz.concurrent.Promise;
|
||||
|
||||
public class Request
|
||||
extends SFTPPacket<Request> {
|
||||
|
||||
private final PacketType type;
|
||||
private final long reqID;
|
||||
private final Future<Response, SFTPException> responseFuture;
|
||||
private final Promise<Response, SFTPException> responsePromise;
|
||||
|
||||
public Request(PacketType type, long reqID) {
|
||||
super(type);
|
||||
this.type = type;
|
||||
this.reqID = reqID;
|
||||
responseFuture = new Future<Response, SFTPException>("sftp / " + reqID, SFTPException.chainer);
|
||||
putInt(reqID);
|
||||
responsePromise = new Promise<Response, SFTPException>("sftp / " + reqID, SFTPException.chainer);
|
||||
putUInt32(reqID);
|
||||
}
|
||||
|
||||
public long getRequestID() {
|
||||
@@ -40,8 +40,8 @@ public class Request
|
||||
return type;
|
||||
}
|
||||
|
||||
public Future<Response, SFTPException> getResponseFuture() {
|
||||
return responseFuture;
|
||||
public Promise<Response, SFTPException> getResponsePromise() {
|
||||
return responsePromise;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,6 +19,8 @@ import java.io.IOException;
|
||||
|
||||
public interface Requester {
|
||||
|
||||
PathHelper getPathHelper();
|
||||
|
||||
Request newRequest(PacketType type);
|
||||
|
||||
Response doRequest(Request req)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -51,11 +51,15 @@ public class Response
|
||||
private final PacketType type;
|
||||
private final long reqID;
|
||||
|
||||
public Response(Buffer<Response> pk, int protocolVersion) {
|
||||
public Response(Buffer<Response> pk, int protocolVersion) throws SFTPException {
|
||||
super(pk);
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.type = readType();
|
||||
this.reqID = readLong();
|
||||
try {
|
||||
this.reqID = readUInt32();
|
||||
} catch (BufferException be) {
|
||||
throw new SFTPException(be);
|
||||
}
|
||||
}
|
||||
|
||||
public int getProtocolVersion() {
|
||||
@@ -70,8 +74,12 @@ public class Response
|
||||
return type;
|
||||
}
|
||||
|
||||
public StatusCode readStatusCode() {
|
||||
return StatusCode.fromInt(readInt());
|
||||
public StatusCode readStatusCode() throws SFTPException {
|
||||
try {
|
||||
return StatusCode.fromInt(readUInt32AsInt());
|
||||
} catch (BufferException be) {
|
||||
throw new SFTPException(be);
|
||||
}
|
||||
}
|
||||
|
||||
public Response ensurePacketTypeIs(PacketType pt)
|
||||
@@ -99,7 +107,11 @@ public class Response
|
||||
|
||||
protected String error(StatusCode sc)
|
||||
throws SFTPException {
|
||||
throw new SFTPException(sc, protocolVersion < 3 ? sc.toString() : readString());
|
||||
try {
|
||||
throw new SFTPException(sc, protocolVersion < 3 ? sc.toString() : readString());
|
||||
} catch (BufferException be) {
|
||||
throw new SFTPException(be);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
package net.schmizz.sshj.sftp;
|
||||
|
||||
import net.schmizz.sshj.xfer.FilePermission;
|
||||
import net.schmizz.sshj.xfer.LocalDestFile;
|
||||
import net.schmizz.sshj.xfer.LocalSourceFile;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -33,13 +35,11 @@ public class SFTPClient
|
||||
/** Logger */
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final SFTPEngine engine;
|
||||
private final SFTPFileTransfer xfer;
|
||||
private final PathHelper pathHelper;
|
||||
protected final SFTPEngine engine;
|
||||
protected final SFTPFileTransfer xfer;
|
||||
|
||||
public SFTPClient(SFTPEngine engine) {
|
||||
this.engine = engine;
|
||||
this.pathHelper = new PathHelper(engine);
|
||||
this.xfer = new SFTPFileTransfer(engine);
|
||||
}
|
||||
|
||||
@@ -90,8 +90,8 @@ public class SFTPClient
|
||||
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())) {
|
||||
for (PathComponents current = engine.getPathHelper().getComponents(path); ;
|
||||
current = engine.getPathHelper().getComponents(current.getParent())) {
|
||||
final FileAttributes attrs = statExistence(current.getPath());
|
||||
if (attrs == null) {
|
||||
dirsToMake.push(current.getPath());
|
||||
@@ -238,6 +238,16 @@ public class SFTPClient
|
||||
xfer.upload(source, dest);
|
||||
}
|
||||
|
||||
public void get(String source, LocalDestFile dest)
|
||||
throws IOException {
|
||||
xfer.download(source, dest);
|
||||
}
|
||||
|
||||
public void put(LocalSourceFile source, String dest)
|
||||
throws IOException {
|
||||
xfer.upload(source, dest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -41,6 +41,8 @@ public class SFTPEngine
|
||||
|
||||
protected volatile int timeout = DEFAULT_TIMEOUT;
|
||||
|
||||
protected final PathHelper pathHelper;
|
||||
|
||||
protected final Subsystem sub;
|
||||
protected final PacketReader reader;
|
||||
protected final OutputStream out;
|
||||
@@ -51,14 +53,19 @@ public class SFTPEngine
|
||||
|
||||
public SFTPEngine(SessionFactory ssh)
|
||||
throws SSHException {
|
||||
this(ssh, PathHelper.DEFAULT_PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
public SFTPEngine(SessionFactory ssh, String pathSep) throws SSHException {
|
||||
sub = ssh.startSession().startSubsystem("sftp");
|
||||
out = sub.getOutputStream();
|
||||
reader = new PacketReader(this);
|
||||
pathHelper = new PathHelper(this, pathSep);
|
||||
}
|
||||
|
||||
public SFTPEngine init()
|
||||
throws IOException {
|
||||
transmit(new SFTPPacket<Request>(PacketType.INIT).putInt(MAX_SUPPORTED_VERSION));
|
||||
transmit(new SFTPPacket<Request>(PacketType.INIT).putUInt32(MAX_SUPPORTED_VERSION));
|
||||
|
||||
final SFTPPacket<Response> response = reader.readPacket();
|
||||
|
||||
@@ -66,7 +73,7 @@ public class SFTPEngine
|
||||
if (type != PacketType.VERSION)
|
||||
throw new SFTPException("Expected INIT packet, received: " + type);
|
||||
|
||||
operativeVersion = response.readInt();
|
||||
operativeVersion = response.readUInt32AsInt();
|
||||
log.info("Server version {}", operativeVersion);
|
||||
if (MAX_SUPPORTED_VERSION < operativeVersion)
|
||||
throw new SFTPException("Server reported incompatible protocol version: " + operativeVersion);
|
||||
@@ -91,6 +98,11 @@ public class SFTPEngine
|
||||
return newRequest(PacketType.EXTENDED).putString(reqName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathHelper getPathHelper() {
|
||||
return pathHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Request newRequest(PacketType type) {
|
||||
return new Request(type, reqID = reqID + 1 & 0xffffffffL);
|
||||
@@ -102,13 +114,13 @@ public class SFTPEngine
|
||||
reader.expectResponseTo(req);
|
||||
log.debug("Sending {}", req);
|
||||
transmit(req);
|
||||
return req.getResponseFuture().get(timeout, TimeUnit.SECONDS);
|
||||
return req.getResponsePromise().retrieve(timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
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)
|
||||
newRequest(PacketType.OPEN).putString(path).putUInt32(OpenMode.toMask(modes)).putFileAttributes(fa)
|
||||
).ensurePacketTypeIs(PacketType.HANDLE).readString();
|
||||
return new RemoteFile(this, path, handle);
|
||||
}
|
||||
@@ -232,7 +244,7 @@ public class SFTPEngine
|
||||
protected static String readSingleName(Response res)
|
||||
throws IOException {
|
||||
res.ensurePacketTypeIs(PacketType.NAME);
|
||||
if (res.readInt() == 1)
|
||||
if (res.readUInt32AsInt() == 1)
|
||||
return res.readString();
|
||||
else
|
||||
throw new SFTPException("Unexpected data in " + res.getType() + " packet");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,15 +18,16 @@ package net.schmizz.sshj.sftp;
|
||||
import net.schmizz.sshj.common.StreamCopier;
|
||||
import net.schmizz.sshj.sftp.Response.StatusCode;
|
||||
import net.schmizz.sshj.xfer.AbstractFileTransfer;
|
||||
import net.schmizz.sshj.xfer.FileSystemFile;
|
||||
import net.schmizz.sshj.xfer.FileTransfer;
|
||||
import net.schmizz.sshj.xfer.FileTransferUtil;
|
||||
import net.schmizz.sshj.xfer.LocalDestFile;
|
||||
import net.schmizz.sshj.xfer.LocalFileFilter;
|
||||
import net.schmizz.sshj.xfer.LocalSourceFile;
|
||||
import net.schmizz.sshj.xfer.TransferListener;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.EnumSet;
|
||||
|
||||
public class SFTPFileTransfer
|
||||
@@ -34,53 +35,49 @@ public class SFTPFileTransfer
|
||||
implements FileTransfer {
|
||||
|
||||
private final SFTPEngine engine;
|
||||
private final PathHelper pathHelper;
|
||||
|
||||
private volatile FileFilter uploadFilter = defaultLocalFilter;
|
||||
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;
|
||||
}
|
||||
};
|
||||
private volatile LocalFileFilter uploadFilter;
|
||||
private volatile RemoteResourceFilter downloadFilter;
|
||||
|
||||
public SFTPFileTransfer(SFTPEngine engine) {
|
||||
this.engine = engine;
|
||||
this.pathHelper = new PathHelper(engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(String source, String dest)
|
||||
throws IOException {
|
||||
new Uploader().upload(new File(source), dest);
|
||||
new Uploader().upload(new FileSystemFile(source), dest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(String source, String dest)
|
||||
throws IOException {
|
||||
final PathComponents pathComponents = pathHelper.getComponents(source);
|
||||
final FileAttributes attributes = engine.stat(source);
|
||||
new Downloader().download(new RemoteResourceInfo(pathComponents, attributes), new File(dest));
|
||||
download(source, new FileSystemFile(dest));
|
||||
}
|
||||
|
||||
public void setUploadFilter(FileFilter uploadFilter) {
|
||||
this.uploadFilter = (this.uploadFilter == null) ? defaultLocalFilter : uploadFilter;
|
||||
@Override
|
||||
public void upload(LocalSourceFile localFile, String remotePath)
|
||||
throws IOException {
|
||||
new Uploader().upload(localFile, remotePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(String source, LocalDestFile dest)
|
||||
throws IOException {
|
||||
final PathComponents pathComponents = engine.getPathHelper().getComponents(source);
|
||||
final FileAttributes attributes = engine.stat(source);
|
||||
new Downloader().download(new RemoteResourceInfo(pathComponents, attributes), dest);
|
||||
}
|
||||
|
||||
public void setUploadFilter(LocalFileFilter uploadFilter) {
|
||||
this.uploadFilter = uploadFilter;
|
||||
}
|
||||
|
||||
public void setDownloadFilter(RemoteResourceFilter downloadFilter) {
|
||||
this.downloadFilter = (this.downloadFilter == null) ? defaultRemoteFilter : downloadFilter;
|
||||
this.downloadFilter = downloadFilter;
|
||||
}
|
||||
|
||||
public FileFilter getUploadFilter() {
|
||||
public LocalFileFilter getUploadFilter() {
|
||||
return uploadFilter;
|
||||
}
|
||||
|
||||
@@ -92,9 +89,9 @@ public class SFTPFileTransfer
|
||||
|
||||
private final TransferListener listener = getTransferListener();
|
||||
|
||||
private void download(final RemoteResourceInfo remote, final File local)
|
||||
private void download(final RemoteResourceInfo remote, final LocalDestFile local)
|
||||
throws IOException {
|
||||
final File adjustedFile;
|
||||
final LocalDestFile adjustedFile;
|
||||
switch (remote.getAttributes().getType()) {
|
||||
case DIRECTORY:
|
||||
listener.startedDir(remote.getName());
|
||||
@@ -103,7 +100,7 @@ public class SFTPFileTransfer
|
||||
break;
|
||||
case UNKNOWN:
|
||||
log.warn("Server did not supply information about the type of file at `{}` " +
|
||||
"-- assuming it is a regular file!", remote.getPath());
|
||||
"-- assuming it is a regular file!", remote.getPath());
|
||||
case REGULAR:
|
||||
listener.startedFile(remote.getName(), remote.getAttributes().getSize());
|
||||
adjustedFile = downloadFile(remote, local);
|
||||
@@ -116,30 +113,33 @@ public class SFTPFileTransfer
|
||||
|
||||
}
|
||||
|
||||
private File downloadDir(final RemoteResourceInfo remote, final File local)
|
||||
private LocalDestFile downloadDir(final RemoteResourceInfo remote, final LocalDestFile local)
|
||||
throws IOException {
|
||||
final File adjusted = FileTransferUtil.getTargetDirectory(local, remote.getName());
|
||||
final LocalDestFile adjusted = local.getTargetDirectory(remote.getName());
|
||||
final RemoteDirectory rd = engine.openDir(remote.getPath());
|
||||
try {
|
||||
for (RemoteResourceInfo rri : rd.scan(getDownloadFilter()))
|
||||
download(rri, new File(adjusted.getPath(), rri.getName()));
|
||||
download(rri, adjusted.getChild(rri.getName()));
|
||||
} finally {
|
||||
rd.close();
|
||||
}
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
private File downloadFile(final RemoteResourceInfo remote, final File local)
|
||||
private LocalDestFile downloadFile(final RemoteResourceInfo remote, final LocalDestFile local)
|
||||
throws IOException {
|
||||
final File adjusted = FileTransferUtil.getTargetFile(local, remote.getName());
|
||||
final LocalDestFile adjusted = local.getTargetFile(remote.getName());
|
||||
final RemoteFile rf = engine.open(remote.getPath());
|
||||
try {
|
||||
final FileOutputStream fos = new FileOutputStream(adjusted);
|
||||
final OutputStream os = adjusted.getOutputStream();
|
||||
try {
|
||||
StreamCopier.copy(rf.getInputStream(), fos, engine.getSubsystem()
|
||||
.getLocalMaxPacketSize(), false, listener);
|
||||
new StreamCopier(rf.getInputStream(), os)
|
||||
.bufSize(engine.getSubsystem().getLocalMaxPacketSize())
|
||||
.keepFlushing(false)
|
||||
.listener(listener)
|
||||
.copy();
|
||||
} finally {
|
||||
fos.close();
|
||||
os.close();
|
||||
}
|
||||
} finally {
|
||||
rf.close();
|
||||
@@ -147,13 +147,13 @@ public class SFTPFileTransfer
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
private void copyAttributes(final RemoteResourceInfo remote, final File local)
|
||||
private void copyAttributes(final RemoteResourceInfo remote, final LocalDestFile local)
|
||||
throws IOException {
|
||||
final FileAttributes attrs = remote.getAttributes();
|
||||
getModeSetter().setPermissions(local, attrs.getMode().getPermissionsMask());
|
||||
if (getModeSetter().preservesTimes() && attrs.has(FileAttributes.Flag.ACMODTIME)) {
|
||||
getModeSetter().setLastAccessedTime(local, attrs.getAtime());
|
||||
getModeSetter().setLastModifiedTime(local, attrs.getMtime());
|
||||
local.setPermissions(attrs.getMode().getPermissionsMask());
|
||||
if (attrs.has(FileAttributes.Flag.ACMODTIME)) {
|
||||
local.setLastAccessedTime(attrs.getAtime());
|
||||
local.setLastModifiedTime(attrs.getMtime());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ public class SFTPFileTransfer
|
||||
|
||||
private final TransferListener listener = getTransferListener();
|
||||
|
||||
private void upload(File local, String remote)
|
||||
private void upload(LocalSourceFile local, String remote)
|
||||
throws IOException {
|
||||
final String adjustedPath;
|
||||
if (local.isDirectory()) {
|
||||
@@ -171,7 +171,7 @@ public class SFTPFileTransfer
|
||||
adjustedPath = uploadDir(local, remote);
|
||||
listener.finishedDir();
|
||||
} else if (local.isFile()) {
|
||||
listener.startedFile(local.getName(), local.length());
|
||||
listener.startedFile(local.getName(), local.getLength());
|
||||
adjustedPath = uploadFile(local, remote);
|
||||
listener.finishedFile();
|
||||
} else
|
||||
@@ -179,25 +179,28 @@ public class SFTPFileTransfer
|
||||
engine.setAttributes(adjustedPath, getAttributes(local));
|
||||
}
|
||||
|
||||
private String uploadDir(File local, String remote)
|
||||
private String uploadDir(LocalSourceFile local, String remote)
|
||||
throws IOException {
|
||||
final String adjusted = prepareDir(local, remote);
|
||||
for (File f : local.listFiles(getUploadFilter()))
|
||||
for (LocalSourceFile f : local.getChildren(getUploadFilter()))
|
||||
upload(f, adjusted);
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
private String uploadFile(File local, String remote)
|
||||
private String uploadFile(LocalSourceFile local, String remote)
|
||||
throws IOException {
|
||||
final String adjusted = prepareFile(local, remote);
|
||||
final RemoteFile rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE,
|
||||
OpenMode.CREAT,
|
||||
OpenMode.TRUNC));
|
||||
try {
|
||||
final FileInputStream fis = new FileInputStream(local);
|
||||
final InputStream fis = local.getInputStream();
|
||||
try {
|
||||
final int bufSize = engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead();
|
||||
StreamCopier.copy(fis, rf.getOutputStream(), bufSize, false, listener);
|
||||
new StreamCopier(fis, rf.getOutputStream())
|
||||
.bufSize(engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead())
|
||||
.keepFlushing(false)
|
||||
.listener(listener)
|
||||
.copy();
|
||||
} finally {
|
||||
fis.close();
|
||||
}
|
||||
@@ -207,7 +210,7 @@ public class SFTPFileTransfer
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
private String prepareDir(File local, String remote)
|
||||
private String prepareDir(LocalSourceFile local, String remote)
|
||||
throws IOException {
|
||||
final FileAttributes attrs;
|
||||
try {
|
||||
@@ -222,18 +225,18 @@ public class SFTPFileTransfer
|
||||
}
|
||||
|
||||
if (attrs.getMode().getType() == FileMode.Type.DIRECTORY)
|
||||
if (pathHelper.getComponents(remote).getName().equals(local.getName())) {
|
||||
if (engine.getPathHelper().getComponents(remote).getName().equals(local.getName())) {
|
||||
log.debug("probeDir: {} already exists", remote);
|
||||
return remote;
|
||||
} else {
|
||||
log.debug("probeDir: {} already exists, path adjusted for {}", remote, local.getName());
|
||||
return prepareDir(local, PathComponents.adjustForParent(remote, local.getName()));
|
||||
return prepareDir(local, engine.getPathHelper().adjustForParent(remote, local.getName()));
|
||||
}
|
||||
else
|
||||
throw new IOException(attrs.getMode().getType() + " file already exists at " + remote);
|
||||
}
|
||||
|
||||
private String prepareFile(File local, String remote)
|
||||
private String prepareFile(LocalSourceFile local, String remote)
|
||||
throws IOException {
|
||||
final FileAttributes attrs;
|
||||
try {
|
||||
@@ -247,7 +250,7 @@ public class SFTPFileTransfer
|
||||
}
|
||||
if (attrs.getMode().getType() == FileMode.Type.DIRECTORY) {
|
||||
log.debug("probeFile: {} was directory, path adjusted for {}", remote, local.getName());
|
||||
remote = PathComponents.adjustForParent(remote, local.getName());
|
||||
remote = engine.getPathHelper().adjustForParent(remote, local.getName());
|
||||
return remote;
|
||||
} else {
|
||||
log.debug("probeFile: {} is a {} file that will be replaced", remote, attrs.getMode().getType());
|
||||
@@ -255,12 +258,11 @@ public class SFTPFileTransfer
|
||||
}
|
||||
}
|
||||
|
||||
private FileAttributes getAttributes(File local)
|
||||
private FileAttributes getAttributes(LocalSourceFile local)
|
||||
throws IOException {
|
||||
final FileAttributes.Builder builder = new FileAttributes.Builder()
|
||||
.withPermissions(getModeGetter().getPermissions(local));
|
||||
if (getModeGetter().preservesTimes())
|
||||
builder.withAtimeMtime(getModeGetter().getLastAccessTime(local), getModeGetter().getLastModifiedTime(local));
|
||||
final FileAttributes.Builder builder = new FileAttributes.Builder().withPermissions(local.getPermissions());
|
||||
if (local.providesAtimeMtime())
|
||||
builder.withAtimeMtime(local.getLastAccessTime(), local.getLastModifiedTime());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,27 +33,37 @@ public class SFTPPacket<T extends SFTPPacket<T>>
|
||||
putByte(pt.toByte());
|
||||
}
|
||||
|
||||
public FileAttributes readFileAttributes() {
|
||||
public FileAttributes readFileAttributes()
|
||||
throws SFTPException {
|
||||
final FileAttributes.Builder builder = new FileAttributes.Builder();
|
||||
final int mask = readInt();
|
||||
if (FileAttributes.Flag.SIZE.isSet(mask))
|
||||
builder.withSize(readUINT64());
|
||||
if (FileAttributes.Flag.UIDGID.isSet(mask))
|
||||
builder.withUIDGID(readInt(), readInt());
|
||||
if (FileAttributes.Flag.MODE.isSet(mask))
|
||||
builder.withPermissions(readInt());
|
||||
if (FileAttributes.Flag.ACMODTIME.isSet(mask))
|
||||
builder.withAtimeMtime(readInt(), readInt());
|
||||
if (FileAttributes.Flag.EXTENDED.isSet(mask)) {
|
||||
final int extCount = readInt();
|
||||
for (int i = 0; i < extCount; i++)
|
||||
builder.withExtended(readString(), readString());
|
||||
try {
|
||||
final int mask = readUInt32AsInt();
|
||||
if (FileAttributes.Flag.SIZE.isSet(mask))
|
||||
builder.withSize(readUInt64());
|
||||
if (FileAttributes.Flag.UIDGID.isSet(mask))
|
||||
builder.withUIDGID(readUInt32AsInt(), readUInt32AsInt());
|
||||
if (FileAttributes.Flag.MODE.isSet(mask))
|
||||
builder.withPermissions(readUInt32AsInt());
|
||||
if (FileAttributes.Flag.ACMODTIME.isSet(mask))
|
||||
builder.withAtimeMtime(readUInt32AsInt(), readUInt32AsInt());
|
||||
if (FileAttributes.Flag.EXTENDED.isSet(mask)) {
|
||||
final int extCount = readUInt32AsInt();
|
||||
for (int i = 0; i < extCount; i++)
|
||||
builder.withExtended(readString(), readString());
|
||||
}
|
||||
} catch (BufferException be) {
|
||||
throw new SFTPException(be);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public PacketType readType() {
|
||||
return PacketType.fromByte(readByte());
|
||||
public PacketType readType()
|
||||
throws SFTPException {
|
||||
try {
|
||||
return PacketType.fromByte(readByte());
|
||||
} catch (BufferException be) {
|
||||
throw new SFTPException(be);
|
||||
}
|
||||
}
|
||||
|
||||
public T putFileAttributes(FileAttributes fa) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,7 +32,7 @@ public class StatefulSFTPClient
|
||||
}
|
||||
|
||||
private synchronized String cwdify(String path) {
|
||||
return PathComponents.adjustForParent(cwd, path);
|
||||
return engine.getPathHelper().adjustForParent(cwd, path);
|
||||
}
|
||||
|
||||
public synchronized void cd(String dirname)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -90,9 +90,9 @@ public abstract class AbstractSignature
|
||||
| sig[i++] & 0x000000ff;
|
||||
i += j;
|
||||
j = sig[i++] << 24 & 0xff000000
|
||||
| sig[i++] << 16 & 0x00ff0000
|
||||
| sig[i++] << 8 & 0x0000ff00
|
||||
| sig[i++] & 0x000000ff;
|
||||
| sig[i++] << 16 & 0x00ff0000
|
||||
| sig[i++] << 8 & 0x0000ff00
|
||||
| sig[i++] & 0x000000ff;
|
||||
byte[] newSig = new byte[j];
|
||||
System.arraycopy(sig, i, newSig, 0, j);
|
||||
sig = newSig;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,6 +35,7 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport;
|
||||
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.ByteArrayUtils;
|
||||
import net.schmizz.sshj.common.DisconnectReason;
|
||||
import net.schmizz.sshj.common.SSHException;
|
||||
@@ -157,7 +158,12 @@ final class Decoder
|
||||
throws TransportException {
|
||||
cipher.update(inputBuffer.array(), 0, cipherSize);
|
||||
|
||||
final int len = inputBuffer.readInt(); // Read packet length
|
||||
final int len; // Read packet length
|
||||
try {
|
||||
len = inputBuffer.readUInt32AsInt();
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new TransportException(be);
|
||||
}
|
||||
|
||||
if (isInvalidPacketLength(len)) { // Check packet length validity
|
||||
log.info("Error decoding packet (invalid length) {}", inputBuffer.printHex());
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* 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.transport;
|
||||
|
||||
import net.schmizz.sshj.common.DisconnectReason;
|
||||
|
||||
public interface DisconnectListener {
|
||||
|
||||
void notifyDisconnect(DisconnectReason reason);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -63,7 +63,7 @@ final class Encoder
|
||||
private SSHPacket checkHeaderSpace(SSHPacket buffer) {
|
||||
if (buffer.rpos() < 5) {
|
||||
log.warn("Performance cost: when sending a packet, ensure that "
|
||||
+ "5 bytes are available in front of the buffer");
|
||||
+ "5 bytes are available in front of the buffer");
|
||||
SSHPacket nb = new SSHPacket(buffer.available() + 5);
|
||||
nb.rpos(5);
|
||||
nb.wpos(5);
|
||||
@@ -96,8 +96,6 @@ final class Encoder
|
||||
long encode(SSHPacket buffer) {
|
||||
encodeLock.lock();
|
||||
try {
|
||||
buffer = checkHeaderSpace(buffer);
|
||||
|
||||
if (log.isTraceEnabled())
|
||||
log.trace("Encoding packet #{}: {}", seq, buffer.printHex());
|
||||
|
||||
@@ -116,7 +114,7 @@ final class Encoder
|
||||
|
||||
// Put packet header
|
||||
buffer.wpos(startOfPacket);
|
||||
buffer.putInt(packetLen);
|
||||
buffer.putUInt32(packetLen);
|
||||
buffer.putByte((byte) padLen);
|
||||
|
||||
// Now wpos will mark end of padding
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,9 +35,18 @@
|
||||
*/
|
||||
package net.schmizz.sshj.transport;
|
||||
|
||||
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||
import net.schmizz.concurrent.Event;
|
||||
import net.schmizz.concurrent.FutureUtils;
|
||||
import net.schmizz.sshj.common.*;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.DisconnectReason;
|
||||
import net.schmizz.sshj.common.ErrorNotifiable;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.common.Message;
|
||||
import net.schmizz.sshj.common.SSHException;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.common.SSHPacketHandler;
|
||||
import net.schmizz.sshj.common.SecurityUtils;
|
||||
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||
import net.schmizz.sshj.transport.compression.Compression;
|
||||
import net.schmizz.sshj.transport.digest.Digest;
|
||||
@@ -47,6 +56,7 @@ import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
@@ -92,7 +102,8 @@ 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;
|
||||
|
||||
@@ -208,11 +219,11 @@ final class KeyExchanger
|
||||
return;
|
||||
}
|
||||
|
||||
throw new TransportException(DisconnectReason.HOST_KEY_NOT_VERIFIABLE, "Could not verify `"
|
||||
+ KeyType
|
||||
.fromKey(key) + "` host key with fingerprint `" + SecurityUtils.getFingerprint(key)
|
||||
+ "` for `" + transport
|
||||
.getRemoteHost() + "` on port " + transport.getRemotePort());
|
||||
throw new TransportException(DisconnectReason.HOST_KEY_NOT_VERIFIABLE,
|
||||
"Could not verify `" + KeyType.fromKey(key)
|
||||
+ "` host key with fingerprint `" + SecurityUtils.getFingerprint(key)
|
||||
+ "` for `" + transport.getRemoteHost()
|
||||
+ "` on port " + transport.getRemotePort());
|
||||
}
|
||||
|
||||
private void setKexDone() {
|
||||
@@ -223,14 +234,16 @@ final class KeyExchanger
|
||||
|
||||
private void gotKexInit(SSHPacket buf)
|
||||
throws TransportException {
|
||||
Proposal serverProposal = new Proposal(buf);
|
||||
buf.rpos(buf.rpos() - 1);
|
||||
final Proposal serverProposal = new Proposal(buf);
|
||||
negotiatedAlgs = clientProposal.negotiate(serverProposal);
|
||||
log.debug("Negotiated algorithms: {}", negotiatedAlgs);
|
||||
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(), negotiatedAlgs
|
||||
.getKeyExchangeAlgorithm());
|
||||
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(),
|
||||
negotiatedAlgs.getKeyExchangeAlgorithm());
|
||||
try {
|
||||
kex.init(transport, transport.getServerID().getBytes(), transport.getClientID().getBytes(), buf
|
||||
.getCompactData(), clientProposal.getPacket().getCompactData());
|
||||
kex.init(transport,
|
||||
transport.getServerID(), transport.getClientID(),
|
||||
serverProposal.getPacket().getCompactData(), clientProposal.getPacket().getCompactData());
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, e);
|
||||
}
|
||||
@@ -248,7 +261,7 @@ final class KeyExchanger
|
||||
*
|
||||
* @return the resized key
|
||||
*/
|
||||
private static byte[] resizedKey(byte[] E, int blockSize, Digest hash, byte[] K, byte[] H) {
|
||||
private static byte[] resizedKey(byte[] E, int blockSize, Digest hash, BigInteger K, byte[] H) {
|
||||
while (blockSize > E.length) {
|
||||
Buffer.PlainBuffer buffer = new Buffer.PlainBuffer().putMPInt(K).putRawBytes(H).putRawBytes(E);
|
||||
hash.update(buffer.array(), 0, buffer.available());
|
||||
@@ -266,13 +279,15 @@ final class KeyExchanger
|
||||
private void gotNewKeys() {
|
||||
final Digest hash = kex.getHash();
|
||||
|
||||
final byte[] H = kex.getH();
|
||||
|
||||
if (sessionID == null)
|
||||
// session id is 'H' from the first key exchange and does not change thereafter
|
||||
sessionID = Arrays.copyOf(kex.getH(), kex.getH().length);
|
||||
sessionID = H;
|
||||
|
||||
final Buffer.PlainBuffer hashInput = new Buffer.PlainBuffer()
|
||||
.putMPInt(kex.getK())
|
||||
.putRawBytes(kex.getH())
|
||||
.putRawBytes(H)
|
||||
.putByte((byte) 0) // <placeholder>
|
||||
.putRawBytes(sessionID);
|
||||
final int pos = hashInput.available() - sessionID.length - 1; // Position of <placeholder>
|
||||
@@ -321,10 +336,12 @@ final class KeyExchanger
|
||||
negotiatedAlgs.getServer2ClientMACAlgorithm());
|
||||
mac_S2C.init(integrityKey_S2C);
|
||||
|
||||
final Compression compression_S2C = Factory.Named.Util.create(transport.getConfig().getCompressionFactories(),
|
||||
negotiatedAlgs.getServer2ClientCompressionAlgorithm());
|
||||
final Compression compression_C2S = Factory.Named.Util.create(transport.getConfig().getCompressionFactories(),
|
||||
negotiatedAlgs.getClient2ServerCompressionAlgorithm());
|
||||
final Compression compression_S2C =
|
||||
Factory.Named.Util.create(transport.getConfig().getCompressionFactories(),
|
||||
negotiatedAlgs.getServer2ClientCompressionAlgorithm());
|
||||
final Compression compression_C2S =
|
||||
Factory.Named.Util.create(transport.getConfig().getCompressionFactories(),
|
||||
negotiatedAlgs.getClient2ServerCompressionAlgorithm());
|
||||
|
||||
transport.getEncoder().setAlgorithms(cipher_C2S, mac_C2S, compression_C2S);
|
||||
transport.getDecoder().setAlgorithms(cipher_S2C, mac_S2C, compression_S2C);
|
||||
@@ -344,7 +361,6 @@ final class KeyExchanger
|
||||
* having sent the packet ourselves (would cause gotKexInit() to fail)
|
||||
*/
|
||||
kexInitSent.await(transport.getTimeout(), TimeUnit.SECONDS);
|
||||
buf.rpos(buf.rpos() - 1);
|
||||
gotKexInit(buf);
|
||||
expected = Expected.FOLLOWUP;
|
||||
break;
|
||||
@@ -381,7 +397,7 @@ final class KeyExchanger
|
||||
@Override
|
||||
public void notifyError(SSHException error) {
|
||||
log.debug("Got notified of {}", error.toString());
|
||||
FutureUtils.alertAll(error, kexInitSent, done);
|
||||
ErrorDeliveryUtil.alertEvents(error, kexInitSent, done);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,6 +36,7 @@
|
||||
package net.schmizz.sshj.transport;
|
||||
|
||||
import net.schmizz.sshj.Config;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.Factory;
|
||||
import net.schmizz.sshj.common.Message;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
@@ -82,21 +83,26 @@ class Proposal {
|
||||
packet.putString("");
|
||||
|
||||
packet.putBoolean(false); // Optimistic next packet does not follow
|
||||
packet.putInt(0); // "Reserved" for future by spec
|
||||
packet.putUInt32(0); // "Reserved" for future by spec
|
||||
}
|
||||
|
||||
public Proposal(SSHPacket packet) {
|
||||
public Proposal(SSHPacket packet)
|
||||
throws TransportException {
|
||||
this.packet = packet;
|
||||
final int savedPos = packet.rpos();
|
||||
packet.rpos(packet.rpos() + 17); // Skip message ID & cookie
|
||||
kex = fromCommaString(packet.readString());
|
||||
sig = fromCommaString(packet.readString());
|
||||
c2sCipher = fromCommaString(packet.readString());
|
||||
s2cCipher = fromCommaString(packet.readString());
|
||||
c2sMAC = fromCommaString(packet.readString());
|
||||
s2cMAC = fromCommaString(packet.readString());
|
||||
c2sComp = fromCommaString(packet.readString());
|
||||
s2cComp = fromCommaString(packet.readString());
|
||||
try {
|
||||
kex = fromCommaString(packet.readString());
|
||||
sig = fromCommaString(packet.readString());
|
||||
c2sCipher = fromCommaString(packet.readString());
|
||||
s2cCipher = fromCommaString(packet.readString());
|
||||
c2sMAC = fromCommaString(packet.readString());
|
||||
s2cMAC = fromCommaString(packet.readString());
|
||||
c2sComp = fromCommaString(packet.readString());
|
||||
s2cComp = fromCommaString(packet.readString());
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new TransportException(be);
|
||||
}
|
||||
packet.rpos(savedPos);
|
||||
}
|
||||
|
||||
@@ -139,14 +145,14 @@ class Proposal {
|
||||
public NegotiatedAlgorithms negotiate(Proposal other)
|
||||
throws TransportException {
|
||||
return new NegotiatedAlgorithms(
|
||||
firstMatch(this.getKeyExchangeAlgorithms(), other.getKeyExchangeAlgorithms()), //
|
||||
firstMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()), //
|
||||
firstMatch(this.getClient2ServerCipherAlgorithms(), other.getClient2ServerCipherAlgorithms()), //
|
||||
firstMatch(this.getServer2ClientCipherAlgorithms(), other.getServer2ClientCipherAlgorithms()), //
|
||||
firstMatch(this.getClient2ServerMACAlgorithms(), other.getClient2ServerMACAlgorithms()), //
|
||||
firstMatch(this.getServer2ClientMACAlgorithms(), other.getServer2ClientMACAlgorithms()), //
|
||||
firstMatch(this.getClient2ServerCompressionAlgorithms(), other.getClient2ServerCompressionAlgorithms()), //
|
||||
firstMatch(this.getServer2ClientCompressionAlgorithms(), other.getServer2ClientCompressionAlgorithms()) //
|
||||
firstMatch(this.getKeyExchangeAlgorithms(), other.getKeyExchangeAlgorithms()),
|
||||
firstMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()),
|
||||
firstMatch(this.getClient2ServerCipherAlgorithms(), other.getClient2ServerCipherAlgorithms()),
|
||||
firstMatch(this.getServer2ClientCipherAlgorithms(), other.getServer2ClientCipherAlgorithms()),
|
||||
firstMatch(this.getClient2ServerMACAlgorithms(), other.getClient2ServerMACAlgorithms()),
|
||||
firstMatch(this.getServer2ClientMACAlgorithms(), other.getServer2ClientMACAlgorithms()),
|
||||
firstMatch(this.getClient2ServerCompressionAlgorithms(), other.getClient2ServerCompressionAlgorithms()),
|
||||
firstMatch(this.getServer2ClientCompressionAlgorithms(), other.getServer2ClientCompressionAlgorithms())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -44,6 +44,7 @@ import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** Transport layer of the SSH protocol. */
|
||||
public interface Transport
|
||||
@@ -171,12 +172,18 @@ public interface Transport
|
||||
boolean isRunning();
|
||||
|
||||
/**
|
||||
* Joins the thread calling this method to the transport's death. The transport dies of exceptional events.
|
||||
* Joins the thread calling this method to the transport's death.
|
||||
*
|
||||
* @throws TransportException when the transport dies
|
||||
* @throws TransportException if the transport dies of an exception
|
||||
*/
|
||||
void join()
|
||||
throws TransportException;
|
||||
/**
|
||||
* Joins the thread calling this method to the transport's death.
|
||||
*
|
||||
* @throws TransportException if the transport dies of an exception
|
||||
*/
|
||||
void join(int timeout, TimeUnit unit) throws TransportException;
|
||||
|
||||
/** Send a disconnection packet with reason as {@link DisconnectReason#BY_APPLICATION}, and closes this transport. */
|
||||
void disconnect();
|
||||
@@ -211,4 +218,17 @@ public interface Transport
|
||||
*/
|
||||
long write(SSHPacket payload)
|
||||
throws TransportException;
|
||||
|
||||
/**
|
||||
* Specify a {@code listener} that will be notified upon disconnection.
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
void setDisconnectListener(DisconnectListener listener);
|
||||
|
||||
/**
|
||||
* @return the current disconnect listener.
|
||||
*/
|
||||
DisconnectListener getDisconnectListener();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright 2010, 2011 sshj contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user