Compare commits

..

20 Commits

Author SHA1 Message Date
Jeroen van Erp
a18d623f44 Release notes for 0.12.0 2015-04-14 13:14:15 +02:00
Jeroen van Erp
6855873ffd removed release plugin 2015-04-14 12:57:40 +02:00
Jeroen van Erp
2ca8d8b19e Upgraded gradle 2015-04-14 12:03:38 +02:00
Jeroen van Erp
da32b145df Added braces to single-line statements 2015-04-13 11:50:34 +02:00
Jeroen van Erp
8ea6bb4a66 Updated README for fixed #181 and #180 issue 2015-04-01 11:28:14 +02:00
Jeroen van Erp
6cf767528a Added comment to check why that field is needed 2015-04-01 11:25:43 +02:00
Jeroen van Erp
b123a6ae30 Merge pull request #181 from iterate-ch/issue-180
Fix length field for SSH_FXP_WRITE packets.
2015-04-01 11:24:30 +02:00
Jeroen van Erp
4250c61e45 Deprecated Proxy connect methods, moved Sockets utility class to backport package 2015-03-31 09:27:13 +02:00
Jeroen van Erp
ace09fa8c8 Added support for HTTP CONNECT proxies by implementing custom Socket (Fixes #170) 2015-03-30 21:53:04 +02:00
Jeroen van Erp
8398b6e3c3 Revert "Added support for (unauthenticated) HTTP proxies (fixes #170)"
This reverts commit fc535a5e76.
2015-03-30 21:48:07 +02:00
Jeroen van Erp
3c1e0c1629 Updated readme 2015-03-27 16:04:42 +01:00
Andrew Kondratovich
e6c7c17664 Correctly closing channel and socket when LocalPortForwarder fails to open it. (Fix #175) 2015-03-27 16:02:07 +01:00
Jeroen van Erp
1e061aef25 Updated readme with work in progress for next release 2015-03-27 15:30:58 +01:00
Jeroen van Erp
66b772bac1 Compiling for JDK6, fixes #179 and #185 2015-03-27 15:25:45 +01:00
Jeroen van Erp
fc535a5e76 Added support for (unauthenticated) HTTP proxies (fixes #170) 2015-03-27 15:19:04 +01:00
Jeroen van Erp
c7373f05cc Merge pull request #186 from bluekeyes/fix/read-end-of-stream
Detect end-of-stream in TransportImpl#init
2015-03-16 10:34:47 +01:00
Billy Keyes
3ebd2eb363 Detect end-of-stream in TransportImpl#init
OpenSSH will drop connections based on the value of MaxStartups when
there are too many unauthenticated connection. When this happens, reads
on the client socket return -1, which was previously inserted into the
identification buffer, leading to the error in #118.
2015-03-13 15:34:18 -07:00
David Kocher
8638091517 Fix length field for SSH_FXP_WRITE packets. 2015-02-24 15:27:08 +01:00
hierynomus
5fc08a3fc8 back to snapshot 2015-01-23 10:08:51 +01:00
hierynomus
92df7c6924 v0.11.0 2015-01-23 10:07:43 +01:00
12 changed files with 216 additions and 72 deletions

View File

@@ -93,7 +93,13 @@ Fork away!
== Release history
SSHJ 0.11.0 (No date set yet)::
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)::
* 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,13 +4,16 @@ apply plugin: "signing"
apply plugin: "osgi"
group = "com.hierynomus"
version = "0.11.0"
version = "0.12.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.2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-bin.zip

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

View File

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

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

View File

@@ -15,6 +15,9 @@
*/
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;
@@ -45,34 +48,53 @@ 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();
}
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);
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);
}
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;
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));
@@ -80,35 +102,44 @@ 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);
}
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);
}
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);
}
public void disconnect()
throws IOException {
public void disconnect() throws IOException {
if (socket != null) {
socket.close();
socket = null;
@@ -131,7 +162,6 @@ public abstract class SocketClient {
return socket.getLocalPort();
}
public InetAddress getLocalAddress() {
return socket.getLocalAddress();
}
@@ -149,10 +179,11 @@ 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() {
@@ -187,8 +218,7 @@ 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,11 +18,12 @@ 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 {
@@ -32,16 +33,6 @@ 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) {
@@ -54,7 +45,7 @@ public class SocketStreamCopyMonitor
}
} catch (IOException ignored) {
} finally {
IOUtils.closeQuietly(channel, wrapSocket(socket));
IOUtils.closeQuietly(channel, asCloseable(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,6 +30,8 @@ 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 {
@@ -112,11 +114,15 @@ public class LocalPortForwarder {
this.serverSocket = serverSocket;
}
protected DirectTCPIPChannel openChannel(Socket socket)
throws TransportException, ConnectionException {
final DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
chan.open();
return chan;
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;
}
}
/**
@@ -130,7 +136,7 @@ public class LocalPortForwarder {
while (!Thread.currentThread().isInterrupted()) {
final Socket socket = serverSocket.accept();
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
openChannel(socket).start();
startChannel(socket);
}
log.debug("Interrupted!");
}

View File

@@ -88,7 +88,8 @@ public class RemoteFile
throws IOException {
return requester.request(newRequest(PacketType.WRITE)
.putUInt64(fileOffset)
.putUInt32(len - off)
// TODO The SFTP spec claims this field is unneeded...? See #187
.putUInt32(len)
.putRawBytes(data, off, len)
);
}

View File

@@ -158,7 +158,10 @@ public final class TransportImpl
// Read server's ID
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
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);

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 {
static enum Arg {
enum Arg {
SOURCE('f'),
SINK('t'),
RECURSIVE('r'),
@@ -100,13 +100,15 @@ 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());
}
@@ -119,11 +121,13 @@ 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;
@@ -133,36 +137,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)
@@ -170,8 +174,7 @@ 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)