mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-08 16:18:05 +03:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a18d623f44 | ||
|
|
6855873ffd | ||
|
|
2ca8d8b19e | ||
|
|
da32b145df | ||
|
|
8ea6bb4a66 | ||
|
|
6cf767528a | ||
|
|
b123a6ae30 | ||
|
|
4250c61e45 | ||
|
|
ace09fa8c8 | ||
|
|
8398b6e3c3 | ||
|
|
3c1e0c1629 | ||
|
|
e6c7c17664 | ||
|
|
1e061aef25 | ||
|
|
66b772bac1 | ||
|
|
fc535a5e76 | ||
|
|
c7373f05cc | ||
|
|
3ebd2eb363 | ||
|
|
8638091517 | ||
|
|
5fc08a3fc8 | ||
|
|
92df7c6924 |
28
README.adoc
28
README.adoc
@@ -93,14 +93,20 @@ Fork away!
|
|||||||
|
|
||||||
== Release history
|
== Release history
|
||||||
|
|
||||||
* SSHJ 0.11.0 (No date set yet)
|
SSHJ 0.12.0 (2015-04-14)::
|
||||||
** New maven coordinates `com.hierynomus:sshj:0.11.0` as https://github.com/hierynomus[@hierynomus] took over as maintainer of SSHJ
|
* Added support for HTTP proxies when running JDK6 or JDK7, fixes: https://github.com/hierynomus/sshj/issues/170[#170]
|
||||||
** Migrated build system to Gradle 2.2.1
|
* Merged https://github.com/hierynomus/sshj/issues/186[#186]: Fix for detecting end-of-stream
|
||||||
** Merged https://github.com/hierynomus/sshj/issues/150[#150]: Fix for incorrect file handle on some SSH servers, fixes: https://github.com/hierynomus/sshj/issues/54[#54], https://github.com/hierynomus/sshj/issues/119[#119], https://github.com/hierynomus/sshj/issues/168[#168], https://github.com/hierynomus/sshj/issues/169[#169]
|
* Compiling to JDK6, fixes https://github.com/hierynomus/sshj/issues/179[#179] and https://github.com/hierynomus/sshj/issues/185[#185]
|
||||||
** Made `jzlib` optional in OSGi bundling, fixes: https://github.com/hierynomus/sshj/issues/162[#162]
|
* Correctly close socket and channel when LocalPortForwarder fails to open and start the channel (Fixes https://github.com/hierynomus/sshj/issues/175[#175] and https://github.com/hierynomus/sshj/issues/176[#176])
|
||||||
** Improved some log levels, fixes: https://github.com/hierynomus/sshj/issues/161[#161]
|
* Merged https://github.com/hierynomus/sshj/issues/181[#181]: Invalid write packet length when reading with offset (Fixes https://github.com/hierynomus/sshj/issues/180[#180])
|
||||||
** Merged https://github.com/hierynomus/sshj/issues/156[#156], https://github.com/hierynomus/sshj/issues/164[#164], https://github.com/hierynomus/sshj/issues/165[#165]: Fixed block sizes for `hmac-sha2-256` and `hmac-sha2-512`
|
SSHJ 0.11.0 (2015-01-23)::
|
||||||
** Merged https://github.com/hierynomus/sshj/issues/141[#141]: Add proxy support
|
* New maven coordinates `com.hierynomus:sshj:0.11.0` as https://github.com/hierynomus[@hierynomus] took over as maintainer of SSHJ
|
||||||
** Merged https://github.com/hierynomus/sshj/issues/157[#157], https://github.com/hierynomus/sshj/issues/163[#163]: Doc and build fixes
|
* Migrated build system to Gradle 2.2.1
|
||||||
** Upgraded BouncyCastle to 1.51, fixes: https://github.com/hierynomus/sshj/issues/142[#142]
|
* Merged https://github.com/hierynomus/sshj/issues/150[#150]: Fix for incorrect file handle on some SSH servers, fixes: https://github.com/hierynomus/sshj/issues/54[#54], https://github.com/hierynomus/sshj/issues/119[#119], https://github.com/hierynomus/sshj/issues/168[#168], https://github.com/hierynomus/sshj/issues/169[#169]
|
||||||
** Implemented keep-alive with connection drop detection, fixes https://github.com/hierynomus/sshj/issues/166[#166]
|
* Made `jzlib` optional in OSGi bundling, fixes: https://github.com/hierynomus/sshj/issues/162[#162]
|
||||||
|
* Improved some log levels, fixes: https://github.com/hierynomus/sshj/issues/161[#161]
|
||||||
|
* Merged https://github.com/hierynomus/sshj/issues/156[#156], https://github.com/hierynomus/sshj/issues/164[#164], https://github.com/hierynomus/sshj/issues/165[#165]: Fixed block sizes for `hmac-sha2-256` and `hmac-sha2-512`
|
||||||
|
* Merged https://github.com/hierynomus/sshj/issues/141[#141]: Add proxy support
|
||||||
|
* Merged https://github.com/hierynomus/sshj/issues/157[#157], https://github.com/hierynomus/sshj/issues/163[#163]: Doc and build fixes
|
||||||
|
* Upgraded BouncyCastle to 1.51, fixes: https://github.com/hierynomus/sshj/issues/142[#142]
|
||||||
|
* Implemented keep-alive with connection drop detection, fixes https://github.com/hierynomus/sshj/issues/166[#166]
|
||||||
|
|||||||
@@ -4,13 +4,16 @@ apply plugin: "signing"
|
|||||||
apply plugin: "osgi"
|
apply plugin: "osgi"
|
||||||
|
|
||||||
group = "com.hierynomus"
|
group = "com.hierynomus"
|
||||||
version = "0.11.0-SNAPSHOT"
|
version = "0.12.0"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sourceCompatibility = 1.6
|
||||||
|
targetCompatibility = 1.6
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
compile {
|
compile {
|
||||||
transitive = false
|
transitive = false
|
||||||
@@ -155,4 +158,4 @@ uploadArchives {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-bin.zip
|
||||||
|
|||||||
13
src/main/java/com/hierynomus/sshj/backport/JavaVersion.java
Normal file
13
src/main/java/com/hierynomus/sshj/backport/JavaVersion.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package com.hierynomus.sshj.backport;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
public class JavaVersion {
|
||||||
|
public static boolean isJava7OrEarlier() {
|
||||||
|
String property = System.getProperty("java.specification.version");
|
||||||
|
float diff = Float.parseFloat(property) - 1.7f;
|
||||||
|
|
||||||
|
return diff < 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.hierynomus.sshj.backport;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
public class Jdk7HttpProxySocket extends Socket {
|
||||||
|
|
||||||
|
private Proxy httpProxy = null;
|
||||||
|
|
||||||
|
public Jdk7HttpProxySocket(Proxy proxy) {
|
||||||
|
super(proxy.type() == Proxy.Type.HTTP ? Proxy.NO_PROXY : proxy);
|
||||||
|
if (proxy.type() == Proxy.Type.HTTP) {
|
||||||
|
this.httpProxy = proxy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect(SocketAddress endpoint, int timeout) throws IOException {
|
||||||
|
if (httpProxy != null) {
|
||||||
|
connectHttpProxy(endpoint, timeout);
|
||||||
|
} else {
|
||||||
|
super.connect(endpoint, timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectHttpProxy(SocketAddress endpoint, int timeout) throws IOException {
|
||||||
|
super.connect(httpProxy.address(), timeout);
|
||||||
|
|
||||||
|
if (!(endpoint instanceof InetSocketAddress)) {
|
||||||
|
throw new SocketException("Expected an InetSocketAddress to connect to, got: " + endpoint);
|
||||||
|
}
|
||||||
|
InetSocketAddress isa = (InetSocketAddress) endpoint;
|
||||||
|
String httpConnect = "CONNECT " + isa.getHostName() + ":" + isa.getPort() + " HTTP/1.0\n\n";
|
||||||
|
getOutputStream().write(httpConnect.getBytes(Charset.forName("UTF-8")));
|
||||||
|
checkAndFlushProxyResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAndFlushProxyResponse()throws IOException {
|
||||||
|
InputStream socketInput = getInputStream();
|
||||||
|
byte[] tmpBuffer = new byte[512];
|
||||||
|
int len = socketInput.read(tmpBuffer, 0, tmpBuffer.length);
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
throw new SocketException("Empty response from proxy");
|
||||||
|
}
|
||||||
|
|
||||||
|
String proxyResponse = new String(tmpBuffer, 0, len, "UTF-8");
|
||||||
|
|
||||||
|
// Expecting HTTP/1.x 200 OK
|
||||||
|
if (proxyResponse.contains("200")) {
|
||||||
|
// Flush any outstanding message in buffer
|
||||||
|
if (socketInput.available() > 0) {
|
||||||
|
socketInput.skip(socketInput.available());
|
||||||
|
}
|
||||||
|
// Proxy Connect Successful
|
||||||
|
} else {
|
||||||
|
throw new SocketException("Fail to create Socket\nResponse was:" + proxyResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/main/java/com/hierynomus/sshj/backport/Sockets.java
Normal file
26
src/main/java/com/hierynomus/sshj/backport/Sockets.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package com.hierynomus.sshj.backport;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
public class Sockets {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 7 and up have Socket implemented as Closeable, whereas Java6 did not have this inheritance.
|
||||||
|
* @param socket The socket to wrap as Closeable
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Closeable asCloseable(final Socket socket) {
|
||||||
|
if (Closeable.class.isAssignableFrom(socket.getClass())) {
|
||||||
|
return Closeable.class.cast(socket);
|
||||||
|
} else {
|
||||||
|
return new Closeable() {
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj;
|
package net.schmizz.sshj;
|
||||||
|
|
||||||
|
import com.hierynomus.sshj.backport.JavaVersion;
|
||||||
|
import com.hierynomus.sshj.backport.Jdk7HttpProxySocket;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -45,34 +48,53 @@ public abstract class SocketClient {
|
|||||||
this.defaultPort = defaultPort;
|
this.defaultPort = defaultPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(InetAddress host, int port)
|
public void connect(InetAddress host, int port) throws IOException {
|
||||||
throws IOException {
|
|
||||||
socket = socketFactory.createSocket();
|
socket = socketFactory.createSocket();
|
||||||
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
||||||
onConnect();
|
onConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(InetAddress host, int port, Proxy proxy)
|
|
||||||
throws IOException {
|
/**
|
||||||
socket = new Socket(proxy);
|
* Connect to a host via a proxy.
|
||||||
|
* @param host The host address to connect to.
|
||||||
|
* @param port The port to connect to.
|
||||||
|
* @param proxy The proxy to connect via.
|
||||||
|
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
|
||||||
|
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket(java.net.Proxy)} constructor.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void connect(InetAddress host, int port, Proxy proxy) throws IOException {
|
||||||
|
if (JavaVersion.isJava7OrEarlier() && proxy.type() == Proxy.Type.HTTP) {
|
||||||
|
// Java7 and earlier have no support for HTTP Connect proxies, return our custom socket.
|
||||||
|
socket = new Jdk7HttpProxySocket(proxy);
|
||||||
|
} else {
|
||||||
|
socket = new Socket(proxy);
|
||||||
|
}
|
||||||
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
||||||
onConnect();
|
onConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(String hostname, int port)
|
public void connect(String hostname, int port) throws IOException {
|
||||||
throws IOException {
|
|
||||||
this.hostname = hostname;
|
this.hostname = hostname;
|
||||||
connect(InetAddress.getByName(hostname), port);
|
connect(InetAddress.getByName(hostname), port);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(String hostname, int port, Proxy proxy)
|
/**
|
||||||
throws IOException {
|
* Connect to a host via a proxy.
|
||||||
|
* @param hostname The host name to connect to.
|
||||||
|
* @param port The port to connect to.
|
||||||
|
* @param proxy The proxy to connect via.
|
||||||
|
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
|
||||||
|
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket(java.net.Proxy)} constructor.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void connect(String hostname, int port, Proxy proxy) throws IOException {
|
||||||
this.hostname = hostname;
|
this.hostname = hostname;
|
||||||
connect(InetAddress.getByName(hostname), port, proxy);
|
connect(InetAddress.getByName(hostname), port, proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(InetAddress host, int port,
|
public void connect(InetAddress host, int port, InetAddress localAddr, int localPort)
|
||||||
InetAddress localAddr, int localPort)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
socket = socketFactory.createSocket();
|
socket = socketFactory.createSocket();
|
||||||
socket.bind(new InetSocketAddress(localAddr, localPort));
|
socket.bind(new InetSocketAddress(localAddr, localPort));
|
||||||
@@ -80,35 +102,44 @@ public abstract class SocketClient {
|
|||||||
onConnect();
|
onConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(String hostname, int port,
|
public void connect(String hostname, int port, InetAddress localAddr, int localPort) throws IOException {
|
||||||
InetAddress localAddr, int localPort)
|
|
||||||
throws IOException {
|
|
||||||
this.hostname = hostname;
|
this.hostname = hostname;
|
||||||
connect(InetAddress.getByName(hostname), port, localAddr, localPort);
|
connect(InetAddress.getByName(hostname), port, localAddr, localPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(InetAddress host)
|
public void connect(InetAddress host) throws IOException {
|
||||||
throws IOException {
|
|
||||||
connect(host, defaultPort);
|
connect(host, defaultPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(String hostname)
|
public void connect(String hostname) throws IOException {
|
||||||
throws IOException {
|
|
||||||
connect(hostname, defaultPort);
|
connect(hostname, defaultPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(InetAddress host, Proxy proxy)
|
/**
|
||||||
throws IOException {
|
* Connect to a host via a proxy.
|
||||||
|
* @param host The host address to connect to.
|
||||||
|
* @param proxy The proxy to connect via.
|
||||||
|
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
|
||||||
|
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket(java.net.Proxy)} constructor.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void connect(InetAddress host, Proxy proxy) throws IOException {
|
||||||
connect(host, defaultPort, proxy);
|
connect(host, defaultPort, proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(String hostname, Proxy proxy)
|
/**
|
||||||
throws IOException {
|
* Connect to a host via a proxy.
|
||||||
|
* @param hostname The host name to connect to.
|
||||||
|
* @param proxy The proxy to connect via.
|
||||||
|
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
|
||||||
|
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket(java.net.Proxy)} constructor.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void connect(String hostname, Proxy proxy) throws IOException {
|
||||||
connect(hostname, defaultPort, proxy);
|
connect(hostname, defaultPort, proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disconnect()
|
public void disconnect() throws IOException {
|
||||||
throws IOException {
|
|
||||||
if (socket != null) {
|
if (socket != null) {
|
||||||
socket.close();
|
socket.close();
|
||||||
socket = null;
|
socket = null;
|
||||||
@@ -131,7 +162,6 @@ public abstract class SocketClient {
|
|||||||
return socket.getLocalPort();
|
return socket.getLocalPort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public InetAddress getLocalAddress() {
|
public InetAddress getLocalAddress() {
|
||||||
return socket.getLocalAddress();
|
return socket.getLocalAddress();
|
||||||
}
|
}
|
||||||
@@ -149,10 +179,11 @@ public abstract class SocketClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setSocketFactory(SocketFactory factory) {
|
public void setSocketFactory(SocketFactory factory) {
|
||||||
if (factory == null)
|
if (factory == null) {
|
||||||
socketFactory = SocketFactory.getDefault();
|
socketFactory = SocketFactory.getDefault();
|
||||||
else
|
} else {
|
||||||
socketFactory = factory;
|
socketFactory = factory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SocketFactory getSocketFactory() {
|
public SocketFactory getSocketFactory() {
|
||||||
@@ -187,8 +218,7 @@ public abstract class SocketClient {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onConnect()
|
void onConnect() throws IOException {
|
||||||
throws IOException {
|
|
||||||
socket.setSoTimeout(timeout);
|
socket.setSoTimeout(timeout);
|
||||||
input = socket.getInputStream();
|
input = socket.getInputStream();
|
||||||
output = socket.getOutputStream();
|
output = socket.getOutputStream();
|
||||||
|
|||||||
@@ -18,11 +18,12 @@ package net.schmizz.sshj.connection.channel;
|
|||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.sshj.common.IOUtils;
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static com.hierynomus.sshj.backport.Sockets.asCloseable;
|
||||||
|
|
||||||
public class SocketStreamCopyMonitor
|
public class SocketStreamCopyMonitor
|
||||||
extends Thread {
|
extends Thread {
|
||||||
|
|
||||||
@@ -32,16 +33,6 @@ public class SocketStreamCopyMonitor
|
|||||||
setDaemon(true);
|
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,
|
public static void monitor(final int frequency, final TimeUnit unit,
|
||||||
final Event<IOException> x, final Event<IOException> y,
|
final Event<IOException> x, final Event<IOException> y,
|
||||||
final Channel channel, final Socket socket) {
|
final Channel channel, final Socket socket) {
|
||||||
@@ -54,7 +45,7 @@ public class SocketStreamCopyMonitor
|
|||||||
}
|
}
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(channel, wrapSocket(socket));
|
IOUtils.closeQuietly(channel, asCloseable(socket));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
|
|||||||
@@ -16,12 +16,12 @@
|
|||||||
package net.schmizz.sshj.connection.channel.direct;
|
package net.schmizz.sshj.connection.channel.direct;
|
||||||
|
|
||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
|
import net.schmizz.sshj.common.IOUtils;
|
||||||
import net.schmizz.sshj.common.SSHPacket;
|
import net.schmizz.sshj.common.SSHPacket;
|
||||||
import net.schmizz.sshj.common.StreamCopier;
|
import net.schmizz.sshj.common.StreamCopier;
|
||||||
import net.schmizz.sshj.connection.Connection;
|
import net.schmizz.sshj.connection.Connection;
|
||||||
import net.schmizz.sshj.connection.ConnectionException;
|
|
||||||
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -30,6 +30,8 @@ import java.net.ServerSocket;
|
|||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static com.hierynomus.sshj.backport.Sockets.asCloseable;
|
||||||
|
|
||||||
public class LocalPortForwarder {
|
public class LocalPortForwarder {
|
||||||
|
|
||||||
public static class Parameters {
|
public static class Parameters {
|
||||||
@@ -112,11 +114,15 @@ public class LocalPortForwarder {
|
|||||||
this.serverSocket = serverSocket;
|
this.serverSocket = serverSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DirectTCPIPChannel openChannel(Socket socket)
|
private void startChannel(Socket socket) throws IOException {
|
||||||
throws TransportException, ConnectionException {
|
DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
|
||||||
final DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
|
try {
|
||||||
chan.open();
|
chan.open();
|
||||||
return chan;
|
chan.start();
|
||||||
|
} catch (IOException e) {
|
||||||
|
IOUtils.closeQuietly(chan, asCloseable(socket));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,7 +136,7 @@ public class LocalPortForwarder {
|
|||||||
while (!Thread.currentThread().isInterrupted()) {
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
final Socket socket = serverSocket.accept();
|
final Socket socket = serverSocket.accept();
|
||||||
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
|
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
|
||||||
openChannel(socket).start();
|
startChannel(socket);
|
||||||
}
|
}
|
||||||
log.debug("Interrupted!");
|
log.debug("Interrupted!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,8 @@ public class RemoteFile
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
return requester.request(newRequest(PacketType.WRITE)
|
return requester.request(newRequest(PacketType.WRITE)
|
||||||
.putUInt64(fileOffset)
|
.putUInt64(fileOffset)
|
||||||
.putUInt32(len - off)
|
// TODO The SFTP spec claims this field is unneeded...? See #187
|
||||||
|
.putUInt32(len)
|
||||||
.putRawBytes(data, off, len)
|
.putRawBytes(data, off, len)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,7 +158,10 @@ public final class TransportImpl
|
|||||||
// Read server's ID
|
// Read server's ID
|
||||||
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
|
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
|
||||||
while ((serverID = readIdentification(buf)).isEmpty()) {
|
while ((serverID = readIdentification(buf)).isEmpty()) {
|
||||||
buf.putByte((byte) connInfo.in.read());
|
int b = connInfo.in.read();
|
||||||
|
if (b == -1)
|
||||||
|
throw new TransportException("Server closed connection during identification exchange");
|
||||||
|
buf.putByte((byte) b);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Server identity string: {}", serverID);
|
log.info("Server identity string: {}", serverID);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import java.util.List;
|
|||||||
/** @see <a href="http://blogs.sun.com/janp/entry/how_the_scp_protocol_works">SCP Protocol</a> */
|
/** @see <a href="http://blogs.sun.com/janp/entry/how_the_scp_protocol_works">SCP Protocol</a> */
|
||||||
class SCPEngine {
|
class SCPEngine {
|
||||||
|
|
||||||
static enum Arg {
|
enum Arg {
|
||||||
SOURCE('f'),
|
SOURCE('f'),
|
||||||
SINK('t'),
|
SINK('t'),
|
||||||
RECURSIVE('r'),
|
RECURSIVE('r'),
|
||||||
@@ -100,13 +100,15 @@ class SCPEngine {
|
|||||||
void execSCPWith(List<Arg> args, String path)
|
void execSCPWith(List<Arg> args, String path)
|
||||||
throws SSHException {
|
throws SSHException {
|
||||||
final StringBuilder cmd = new StringBuilder(SCP_COMMAND);
|
final StringBuilder cmd = new StringBuilder(SCP_COMMAND);
|
||||||
for (Arg arg : args)
|
for (Arg arg : args) {
|
||||||
cmd.append(" ").append(arg);
|
cmd.append(" ").append(arg);
|
||||||
|
}
|
||||||
cmd.append(" ");
|
cmd.append(" ");
|
||||||
if (path == null || path.isEmpty())
|
if (path == null || path.isEmpty()) {
|
||||||
cmd.append(".");
|
cmd.append(".");
|
||||||
else
|
} else {
|
||||||
cmd.append("'").append(path.replaceAll("'", "\\'")).append("'");
|
cmd.append("'").append(path.replaceAll("'", "\\'")).append("'");
|
||||||
|
}
|
||||||
scp = host.startSession().exec(cmd.toString());
|
scp = host.startSession().exec(cmd.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,11 +121,13 @@ class SCPEngine {
|
|||||||
exitStatus = scp.getExitStatus();
|
exitStatus = scp.getExitStatus();
|
||||||
if (scp.getExitStatus() != 0)
|
if (scp.getExitStatus() != 0)
|
||||||
log.warn("SCP exit status: {}", scp.getExitStatus());
|
log.warn("SCP exit status: {}", scp.getExitStatus());
|
||||||
} else
|
} else {
|
||||||
exitStatus = -1;
|
exitStatus = -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (scp.getExitSignal() != null)
|
if (scp.getExitSignal() != null) {
|
||||||
log.warn("SCP exit signal: {}", scp.getExitSignal());
|
log.warn("SCP exit signal: {}", scp.getExitSignal());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scp = null;
|
scp = null;
|
||||||
@@ -133,36 +137,36 @@ class SCPEngine {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
int x;
|
int x;
|
||||||
while ((x = scp.getInputStream().read()) != LF)
|
while ((x = scp.getInputStream().read()) != LF) {
|
||||||
if (x == -1) {
|
if (x == -1) {
|
||||||
if (baos.size() == 0)
|
if (baos.size() == 0) {
|
||||||
return "";
|
return "";
|
||||||
else
|
} else {
|
||||||
throw new IOException("EOF while reading message");
|
throw new IOException("EOF while reading message");
|
||||||
} else
|
}
|
||||||
|
} else {
|
||||||
baos.write(x);
|
baos.write(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
final String msg = baos.toString(IOUtils.UTF8.displayName());
|
final String msg = baos.toString(IOUtils.UTF8.displayName());
|
||||||
log.debug("Read message: `{}`", msg);
|
log.debug("Read message: `{}`", msg);
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendMessage(String msg)
|
void sendMessage(String msg) throws IOException {
|
||||||
throws IOException {
|
|
||||||
log.debug("Sending message: {}", msg);
|
log.debug("Sending message: {}", msg);
|
||||||
scp.getOutputStream().write((msg + LF).getBytes(IOUtils.UTF8));
|
scp.getOutputStream().write((msg + LF).getBytes(IOUtils.UTF8));
|
||||||
scp.getOutputStream().flush();
|
scp.getOutputStream().flush();
|
||||||
check("Message ACK received");
|
check("Message ACK received");
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal(String what)
|
void signal(String what) throws IOException {
|
||||||
throws IOException {
|
|
||||||
log.debug("Signalling: {}", what);
|
log.debug("Signalling: {}", what);
|
||||||
scp.getOutputStream().write(0);
|
scp.getOutputStream().write(0);
|
||||||
scp.getOutputStream().flush();
|
scp.getOutputStream().flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
long transferToRemote(StreamCopier.Listener listener, InputStream src, long length)
|
long transferToRemote(StreamCopier.Listener listener, InputStream src, long length) throws IOException {
|
||||||
throws IOException {
|
|
||||||
return new StreamCopier(src, scp.getOutputStream())
|
return new StreamCopier(src, scp.getOutputStream())
|
||||||
.bufSize(scp.getRemoteMaxPacketSize()).length(length)
|
.bufSize(scp.getRemoteMaxPacketSize()).length(length)
|
||||||
.keepFlushing(false)
|
.keepFlushing(false)
|
||||||
@@ -170,8 +174,7 @@ class SCPEngine {
|
|||||||
.copy();
|
.copy();
|
||||||
}
|
}
|
||||||
|
|
||||||
long transferFromRemote(StreamCopier.Listener listener, OutputStream dest, long length)
|
long transferFromRemote(StreamCopier.Listener listener, OutputStream dest, long length) throws IOException {
|
||||||
throws IOException {
|
|
||||||
return new StreamCopier(scp.getInputStream(), dest)
|
return new StreamCopier(scp.getInputStream(), dest)
|
||||||
.bufSize(scp.getLocalMaxPacketSize()).length(length)
|
.bufSize(scp.getLocalMaxPacketSize()).length(length)
|
||||||
.keepFlushing(false)
|
.keepFlushing(false)
|
||||||
|
|||||||
Reference in New Issue
Block a user