Compare commits

..

1 Commits

Author SHA1 Message Date
hierynomus
936c6c8008 v0.11.0 2015-01-23 10:01:50 +01:00
12 changed files with 72 additions and 216 deletions

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
};
}
}
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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!");
}

View File

@@ -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)
);
}

View File

@@ -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);

View File

@@ -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)