mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-07 15:50:57 +03:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
936c6c8008 |
@@ -93,13 +93,7 @@ Fork away!
|
||||
|
||||
== Release history
|
||||
|
||||
SSHJ 0.12.0 (2015-04-14)::
|
||||
* Added support for HTTP proxies when running JDK6 or JDK7, fixes: https://github.com/hierynomus/sshj/issues/170[#170]
|
||||
* Merged https://github.com/hierynomus/sshj/issues/186[#186]: Fix for detecting end-of-stream
|
||||
* Compiling to JDK6, fixes https://github.com/hierynomus/sshj/issues/179[#179] and https://github.com/hierynomus/sshj/issues/185[#185]
|
||||
* 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])
|
||||
* 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])
|
||||
SSHJ 0.11.0 (2015-01-23)::
|
||||
SSHJ 0.11.0 (No date set yet)::
|
||||
* New maven coordinates `com.hierynomus:sshj:0.11.0` as https://github.com/hierynomus[@hierynomus] took over as maintainer of SSHJ
|
||||
* Migrated build system to Gradle 2.2.1
|
||||
* 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]
|
||||
|
||||
@@ -4,16 +4,13 @@ apply plugin: "signing"
|
||||
apply plugin: "osgi"
|
||||
|
||||
group = "com.hierynomus"
|
||||
version = "0.12.0"
|
||||
version = "0.11.0"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.6
|
||||
targetCompatibility = 1.6
|
||||
|
||||
configurations {
|
||||
compile {
|
||||
transitive = false
|
||||
|
||||
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
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
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,9 +15,6 @@
|
||||
*/
|
||||
package net.schmizz.sshj;
|
||||
|
||||
import com.hierynomus.sshj.backport.JavaVersion;
|
||||
import com.hierynomus.sshj.backport.Jdk7HttpProxySocket;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -48,53 +45,34 @@ public abstract class SocketClient {
|
||||
this.defaultPort = defaultPort;
|
||||
}
|
||||
|
||||
public void connect(InetAddress host, int port) throws IOException {
|
||||
public void connect(InetAddress host, int port)
|
||||
throws IOException {
|
||||
socket = socketFactory.createSocket();
|
||||
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
||||
onConnect();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
public void connect(InetAddress host, int port, Proxy proxy)
|
||||
throws IOException {
|
||||
socket = new Socket(proxy);
|
||||
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
||||
onConnect();
|
||||
}
|
||||
|
||||
public void connect(String hostname, int port) throws IOException {
|
||||
public void connect(String hostname, int port)
|
||||
throws IOException {
|
||||
this.hostname = hostname;
|
||||
connect(InetAddress.getByName(hostname), port);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
public void connect(String hostname, int port, Proxy proxy)
|
||||
throws IOException {
|
||||
this.hostname = hostname;
|
||||
connect(InetAddress.getByName(hostname), port, proxy);
|
||||
}
|
||||
|
||||
public void connect(InetAddress host, int port, InetAddress localAddr, int localPort)
|
||||
public void connect(InetAddress host, int port,
|
||||
InetAddress localAddr, int localPort)
|
||||
throws IOException {
|
||||
socket = socketFactory.createSocket();
|
||||
socket.bind(new InetSocketAddress(localAddr, localPort));
|
||||
@@ -102,44 +80,35 @@ public abstract class SocketClient {
|
||||
onConnect();
|
||||
}
|
||||
|
||||
public void connect(String hostname, int port, InetAddress localAddr, int localPort) throws IOException {
|
||||
public void connect(String hostname, int port,
|
||||
InetAddress localAddr, int localPort)
|
||||
throws IOException {
|
||||
this.hostname = hostname;
|
||||
connect(InetAddress.getByName(hostname), port, localAddr, localPort);
|
||||
}
|
||||
|
||||
public void connect(InetAddress host) throws IOException {
|
||||
public void connect(InetAddress host)
|
||||
throws IOException {
|
||||
connect(host, defaultPort);
|
||||
}
|
||||
|
||||
public void connect(String hostname) throws IOException {
|
||||
public void connect(String hostname)
|
||||
throws IOException {
|
||||
connect(hostname, defaultPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
public void connect(InetAddress host, Proxy proxy)
|
||||
throws IOException {
|
||||
connect(host, defaultPort, proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
public void connect(String hostname, Proxy proxy)
|
||||
throws IOException {
|
||||
connect(hostname, defaultPort, proxy);
|
||||
}
|
||||
|
||||
public void disconnect() throws IOException {
|
||||
public void disconnect()
|
||||
throws IOException {
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
socket = null;
|
||||
@@ -162,6 +131,7 @@ public abstract class SocketClient {
|
||||
return socket.getLocalPort();
|
||||
}
|
||||
|
||||
|
||||
public InetAddress getLocalAddress() {
|
||||
return socket.getLocalAddress();
|
||||
}
|
||||
@@ -179,11 +149,10 @@ public abstract class SocketClient {
|
||||
}
|
||||
|
||||
public void setSocketFactory(SocketFactory factory) {
|
||||
if (factory == null) {
|
||||
if (factory == null)
|
||||
socketFactory = SocketFactory.getDefault();
|
||||
} else {
|
||||
else
|
||||
socketFactory = factory;
|
||||
}
|
||||
}
|
||||
|
||||
public SocketFactory getSocketFactory() {
|
||||
@@ -218,7 +187,8 @@ public abstract class SocketClient {
|
||||
return output;
|
||||
}
|
||||
|
||||
void onConnect() throws IOException {
|
||||
void onConnect()
|
||||
throws IOException {
|
||||
socket.setSoTimeout(timeout);
|
||||
input = socket.getInputStream();
|
||||
output = socket.getOutputStream();
|
||||
|
||||
@@ -18,12 +18,11 @@ 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;
|
||||
|
||||
import static com.hierynomus.sshj.backport.Sockets.asCloseable;
|
||||
|
||||
public class SocketStreamCopyMonitor
|
||||
extends Thread {
|
||||
|
||||
@@ -33,6 +32,16 @@ public class SocketStreamCopyMonitor
|
||||
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) {
|
||||
@@ -45,7 +54,7 @@ public class SocketStreamCopyMonitor
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
} finally {
|
||||
IOUtils.closeQuietly(channel, asCloseable(socket));
|
||||
IOUtils.closeQuietly(channel, wrapSocket(socket));
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
package net.schmizz.sshj.connection.channel.direct;
|
||||
|
||||
import net.schmizz.concurrent.Event;
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.common.StreamCopier;
|
||||
import net.schmizz.sshj.connection.Connection;
|
||||
import net.schmizz.sshj.connection.ConnectionException;
|
||||
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
||||
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -30,8 +30,6 @@ import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.hierynomus.sshj.backport.Sockets.asCloseable;
|
||||
|
||||
public class LocalPortForwarder {
|
||||
|
||||
public static class Parameters {
|
||||
@@ -114,15 +112,11 @@ public class LocalPortForwarder {
|
||||
this.serverSocket = serverSocket;
|
||||
}
|
||||
|
||||
private void startChannel(Socket socket) throws IOException {
|
||||
DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
|
||||
try {
|
||||
chan.open();
|
||||
chan.start();
|
||||
} catch (IOException e) {
|
||||
IOUtils.closeQuietly(chan, asCloseable(socket));
|
||||
throw e;
|
||||
}
|
||||
protected DirectTCPIPChannel openChannel(Socket socket)
|
||||
throws TransportException, ConnectionException {
|
||||
final DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
|
||||
chan.open();
|
||||
return chan;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,7 +130,7 @@ public class LocalPortForwarder {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
final Socket socket = serverSocket.accept();
|
||||
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
|
||||
startChannel(socket);
|
||||
openChannel(socket).start();
|
||||
}
|
||||
log.debug("Interrupted!");
|
||||
}
|
||||
|
||||
@@ -88,8 +88,7 @@ public class RemoteFile
|
||||
throws IOException {
|
||||
return requester.request(newRequest(PacketType.WRITE)
|
||||
.putUInt64(fileOffset)
|
||||
// TODO The SFTP spec claims this field is unneeded...? See #187
|
||||
.putUInt32(len)
|
||||
.putUInt32(len - off)
|
||||
.putRawBytes(data, off, len)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -158,10 +158,7 @@ public final class TransportImpl
|
||||
// Read server's ID
|
||||
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
|
||||
while ((serverID = readIdentification(buf)).isEmpty()) {
|
||||
int b = connInfo.in.read();
|
||||
if (b == -1)
|
||||
throw new TransportException("Server closed connection during identification exchange");
|
||||
buf.putByte((byte) b);
|
||||
buf.putByte((byte) connInfo.in.read());
|
||||
}
|
||||
|
||||
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> */
|
||||
class SCPEngine {
|
||||
|
||||
enum Arg {
|
||||
static enum Arg {
|
||||
SOURCE('f'),
|
||||
SINK('t'),
|
||||
RECURSIVE('r'),
|
||||
@@ -100,15 +100,13 @@ class SCPEngine {
|
||||
void execSCPWith(List<Arg> args, String path)
|
||||
throws SSHException {
|
||||
final StringBuilder cmd = new StringBuilder(SCP_COMMAND);
|
||||
for (Arg arg : args) {
|
||||
for (Arg arg : args)
|
||||
cmd.append(" ").append(arg);
|
||||
}
|
||||
cmd.append(" ");
|
||||
if (path == null || path.isEmpty()) {
|
||||
if (path == null || path.isEmpty())
|
||||
cmd.append(".");
|
||||
} else {
|
||||
else
|
||||
cmd.append("'").append(path.replaceAll("'", "\\'")).append("'");
|
||||
}
|
||||
scp = host.startSession().exec(cmd.toString());
|
||||
}
|
||||
|
||||
@@ -121,13 +119,11 @@ class SCPEngine {
|
||||
exitStatus = scp.getExitStatus();
|
||||
if (scp.getExitStatus() != 0)
|
||||
log.warn("SCP exit status: {}", scp.getExitStatus());
|
||||
} else {
|
||||
} else
|
||||
exitStatus = -1;
|
||||
}
|
||||
|
||||
if (scp.getExitSignal() != null) {
|
||||
if (scp.getExitSignal() != null)
|
||||
log.warn("SCP exit signal: {}", scp.getExitSignal());
|
||||
}
|
||||
}
|
||||
|
||||
scp = null;
|
||||
@@ -137,36 +133,36 @@ class SCPEngine {
|
||||
throws IOException {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int x;
|
||||
while ((x = scp.getInputStream().read()) != LF) {
|
||||
while ((x = scp.getInputStream().read()) != LF)
|
||||
if (x == -1) {
|
||||
if (baos.size() == 0) {
|
||||
if (baos.size() == 0)
|
||||
return "";
|
||||
} else {
|
||||
else
|
||||
throw new IOException("EOF while reading message");
|
||||
}
|
||||
} else {
|
||||
} else
|
||||
baos.write(x);
|
||||
}
|
||||
}
|
||||
final String msg = baos.toString(IOUtils.UTF8.displayName());
|
||||
log.debug("Read message: `{}`", msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
void sendMessage(String msg) throws IOException {
|
||||
void sendMessage(String msg)
|
||||
throws IOException {
|
||||
log.debug("Sending message: {}", msg);
|
||||
scp.getOutputStream().write((msg + LF).getBytes(IOUtils.UTF8));
|
||||
scp.getOutputStream().flush();
|
||||
check("Message ACK received");
|
||||
}
|
||||
|
||||
void signal(String what) throws IOException {
|
||||
void signal(String what)
|
||||
throws IOException {
|
||||
log.debug("Signalling: {}", what);
|
||||
scp.getOutputStream().write(0);
|
||||
scp.getOutputStream().flush();
|
||||
}
|
||||
|
||||
long transferToRemote(StreamCopier.Listener listener, InputStream src, long length) throws IOException {
|
||||
long transferToRemote(StreamCopier.Listener listener, InputStream src, long length)
|
||||
throws IOException {
|
||||
return new StreamCopier(src, scp.getOutputStream())
|
||||
.bufSize(scp.getRemoteMaxPacketSize()).length(length)
|
||||
.keepFlushing(false)
|
||||
@@ -174,7 +170,8 @@ class SCPEngine {
|
||||
.copy();
|
||||
}
|
||||
|
||||
long transferFromRemote(StreamCopier.Listener listener, OutputStream dest, long length) throws IOException {
|
||||
long transferFromRemote(StreamCopier.Listener listener, OutputStream dest, long length)
|
||||
throws IOException {
|
||||
return new StreamCopier(scp.getInputStream(), dest)
|
||||
.bufSize(scp.getLocalMaxPacketSize()).length(length)
|
||||
.keepFlushing(false)
|
||||
|
||||
Reference in New Issue
Block a user