minor changes, reformat etc.

This commit is contained in:
Shikhar Bhushan
2010-03-01 23:36:12 +01:00
parent 7106eed7a5
commit 8ec5055615
33 changed files with 470 additions and 388 deletions

View File

@@ -67,16 +67,16 @@ public interface Config {
List<Factory.Named<MAC>> getMACFactories(); List<Factory.Named<MAC>> getMACFactories();
/** /**
* Retrieve the {@link net.schmizz.sshj.transport.random.Random} factory. * Retrieve the {@link Random} factory.
* *
* @return the {@link net.schmizz.sshj.transport.random.Random} factory * @return the {@link Random} factory
*/ */
Factory<Random> getRandomFactory(); Factory<Random> getRandomFactory();
/** /**
* Retrieve the list of named factories for {@link net.schmizz.sshj.signature.Signature} * Retrieve the list of named factories for {@link Signature}
* *
* @return a list of named {@link net.schmizz.sshj.signature.Signature} factories * @return a list of named {@link Signature} factories
*/ */
List<Factory.Named<Signature>> getSignatureFactories(); List<Factory.Named<Signature>> getSignatureFactories();
@@ -87,49 +87,49 @@ public interface Config {
String getVersion(); String getVersion();
/** /**
* Set the named factories for {@link net.schmizz.sshj.transport.cipher.Cipher}. * Set the named factories for {@link Cipher}.
* *
* @param cipherFactories a list of named factories * @param cipherFactories a list of named factories
*/ */
void setCipherFactories(List<Factory.Named<Cipher>> cipherFactories); void setCipherFactories(List<Factory.Named<Cipher>> cipherFactories);
/** /**
* Set the named factories for {@link net.schmizz.sshj.transport.compression.Compression}. * Set the named factories for {@link Compression}.
* *
* @param compressionFactories a list of named factories * @param compressionFactories a list of named factories
*/ */
void setCompressionFactories(List<Factory.Named<Compression>> compressionFactories); void setCompressionFactories(List<Factory.Named<Compression>> compressionFactories);
/** /**
* Set the named factories for {@link net.schmizz.sshj.userauth.keyprovider.FileKeyProvider}. * Set the named factories for {@link FileKeyProvider}.
* *
* @param fileKeyProviderFactories a list of named factories * @param fileKeyProviderFactories a list of named factories
*/ */
void setFileKeyProviderFactories(List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories); void setFileKeyProviderFactories(List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories);
/** /**
* Set the named factories for {@link net.schmizz.sshj.transport.kex.KeyExchange}. * Set the named factories for {@link KeyExchange}.
* *
* @param kexFactories a list of named factories * @param kexFactories a list of named factories
*/ */
void setKeyExchangeFactories(List<Factory.Named<KeyExchange>> kexFactories); void setKeyExchangeFactories(List<Factory.Named<KeyExchange>> kexFactories);
/** /**
* Set the named factories for {@link net.schmizz.sshj.transport.mac.MAC}. * Set the named factories for {@link MAC}.
* *
* @param macFactories a list of named factories * @param macFactories a list of named factories
*/ */
void setMACFactories(List<Factory.Named<MAC>> macFactories); void setMACFactories(List<Factory.Named<MAC>> macFactories);
/** /**
* Set the factory for {@link net.schmizz.sshj.transport.random.Random}. * Set the factory for {@link Random}.
* *
* @param randomFactory the factory * @param randomFactory the factory
*/ */
void setRandomFactory(Factory<Random> randomFactory); void setRandomFactory(Factory<Random> randomFactory);
/** /**
* Set the named factories for {@link net.schmizz.sshj.signature.Signature}. * Set the named factories for {@link Signature}.
* *
* @param signatureFactories a list of named factories * @param signatureFactories a list of named factories
*/ */

View File

@@ -99,8 +99,7 @@ import java.util.List;
* <p/> * <p/>
* {@link #startSession()} caters to the most typical use case of starting a {@code session} channel and executing a * {@link #startSession()} caters to the most typical use case of starting a {@code session} channel and executing a
* remote command, starting a subsystem, etc. If you wish to request X11 forwarding for some session, first {@link * remote command, starting a subsystem, etc. If you wish to request X11 forwarding for some session, first {@link
* #registerX11Forwarder(net.schmizz.sshj.connection.channel.forwarded.ConnectListener) register} a {@link * #registerX11Forwarder(ConnectListener) register} a {@link ConnectListener} for {@code x11} channels.
* net.schmizz.sshj.connection.channel.forwarded.ConnectListener} for {@code x11} channels.
* <p/> * <p/>
* {@link #newLocalPortForwarder Local} and {@link #getRemotePortForwarder() remote} port forwarding is possible. There * {@link #newLocalPortForwarder Local} and {@link #getRemotePortForwarder() remote} port forwarding is possible. There
* are also utility method for easily creating {@link #newSCPFileTransfer SCP} and {@link #newSFTPClient() SFTP} * are also utility method for easily creating {@link #newSCPFileTransfer SCP} and {@link #newSFTPClient() SFTP}
@@ -127,7 +126,9 @@ import java.util.List;
* Where a password or passphrase is required, if you're extra-paranoid use the {@code char[]} based method. The {@code * Where a password or passphrase is required, if you're extra-paranoid use the {@code char[]} based method. The {@code
* char[]} will be blanked out after use. * char[]} will be blanked out after use.
*/ */
public class SSHClient extends SocketClient implements SessionFactory { public class SSHClient
extends SocketClient
implements SessionFactory {
/** Default port for SSH */ /** Default port for SSH */
public static final int DEFAULT_PORT = 22; public static final int DEFAULT_PORT = 22;
@@ -180,7 +181,7 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @param port the port for which the {@code fingerprint} applies * @param port the port for which the {@code fingerprint} applies
* @param fingerprint expected fingerprint in colon-delimited format (16 octets in hex delimited by a colon) * @param fingerprint expected fingerprint in colon-delimited format (16 octets in hex delimited by a colon)
* *
* @see net.schmizz.sshj.common.SecurityUtils#getFingerprint * @see SecurityUtils#getFingerprint
*/ */
public void addHostKeyVerifier(final String host, final int port, final String fingerprint) { public void addHostKeyVerifier(final String host, final int port, final String fingerprint) {
addHostKeyVerifier(new HostKeyVerifier() { addHostKeyVerifier(new HostKeyVerifier() {
@@ -199,7 +200,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @throws UserAuthException in case of authentication failure * @throws UserAuthException in case of authentication failure
* @throws TransportException if there was a transport-layer error * @throws TransportException if there was a transport-layer error
*/ */
public void auth(String username, AuthMethod... methods) throws UserAuthException, TransportException { public void auth(String username, AuthMethod... methods)
throws UserAuthException, TransportException {
assert isConnected(); assert isConnected();
auth(username, Arrays.<AuthMethod>asList(methods)); auth(username, Arrays.<AuthMethod>asList(methods));
} }
@@ -213,7 +215,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @throws UserAuthException in case of authentication failure * @throws UserAuthException in case of authentication failure
* @throws TransportException if there was a transport-layer error * @throws TransportException if there was a transport-layer error
*/ */
public void auth(String username, Iterable<AuthMethod> methods) throws UserAuthException, TransportException { public void auth(String username, Iterable<AuthMethod> methods)
throws UserAuthException, TransportException {
assert isConnected(); assert isConnected();
auth.authenticate(username, conn, methods); auth.authenticate(username, conn, methods);
} }
@@ -228,7 +231,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @throws UserAuthException in case of authentication failure * @throws UserAuthException in case of authentication failure
* @throws TransportException if there was a transport-layer error * @throws TransportException if there was a transport-layer error
*/ */
public void authPassword(String username, char[] password) throws UserAuthException, TransportException { public void authPassword(String username, char[] password)
throws UserAuthException, TransportException {
authPassword(username, PasswordUtils.createOneOff(password)); authPassword(username, PasswordUtils.createOneOff(password));
} }
@@ -236,12 +240,13 @@ public class SSHClient extends SocketClient implements SessionFactory {
* Authenticate {@code username} using the {@code "password"} authentication method. * Authenticate {@code username} using the {@code "password"} authentication method.
* *
* @param username user to authenticate * @param username user to authenticate
* @param pfinder the {@link net.schmizz.sshj.userauth.password.PasswordFinder} to use for authentication * @param pfinder the {@link PasswordFinder} to use for authentication
* *
* @throws UserAuthException in case of authentication failure * @throws UserAuthException in case of authentication failure
* @throws TransportException if there was a transport-layer error * @throws TransportException if there was a transport-layer error
*/ */
public void authPassword(String username, PasswordFinder pfinder) throws UserAuthException, TransportException { public void authPassword(String username, PasswordFinder pfinder)
throws UserAuthException, TransportException {
auth(username, new AuthPassword(pfinder)); auth(username, new AuthPassword(pfinder));
} }
@@ -254,7 +259,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @throws UserAuthException in case of authentication failure * @throws UserAuthException in case of authentication failure
* @throws TransportException if there was a transport-layer error * @throws TransportException if there was a transport-layer error
*/ */
public void authPassword(String username, String password) throws UserAuthException, TransportException { public void authPassword(String username, String password)
throws UserAuthException, TransportException {
authPassword(username, password.toCharArray()); authPassword(username, password.toCharArray());
} }
@@ -269,7 +275,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @throws UserAuthException in case of authentication failure * @throws UserAuthException in case of authentication failure
* @throws TransportException if there was a transport-layer error * @throws TransportException if there was a transport-layer error
*/ */
public void authPublickey(String username) throws UserAuthException, TransportException { public void authPublickey(String username)
throws UserAuthException, TransportException {
String base = System.getProperty("user.home") + File.separator + ".ssh" + File.separator; String base = System.getProperty("user.home") + File.separator + ".ssh" + File.separator;
authPublickey(username, base + "id_rsa", base + "id_dsa"); authPublickey(username, base + "id_rsa", base + "id_dsa");
} }
@@ -287,7 +294,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @throws UserAuthException in case of authentication failure * @throws UserAuthException in case of authentication failure
* @throws TransportException if there was a transport-layer error * @throws TransportException if there was a transport-layer error
*/ */
public void authPublickey(String username, Iterable<KeyProvider> keyProviders) throws UserAuthException, public void authPublickey(String username, Iterable<KeyProvider> keyProviders)
throws UserAuthException,
TransportException { TransportException {
List<AuthMethod> am = new LinkedList<AuthMethod>(); List<AuthMethod> am = new LinkedList<AuthMethod>();
for (KeyProvider kp : keyProviders) for (KeyProvider kp : keyProviders)
@@ -308,7 +316,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @throws UserAuthException in case of authentication failure * @throws UserAuthException in case of authentication failure
* @throws TransportException if there was a transport-layer error * @throws TransportException if there was a transport-layer error
*/ */
public void authPublickey(String username, KeyProvider... keyProviders) throws UserAuthException, public void authPublickey(String username, KeyProvider... keyProviders)
throws UserAuthException,
TransportException { TransportException {
authPublickey(username, Arrays.<KeyProvider>asList(keyProviders)); authPublickey(username, Arrays.<KeyProvider>asList(keyProviders));
} }
@@ -329,7 +338,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @throws UserAuthException in case of authentication failure * @throws UserAuthException in case of authentication failure
* @throws TransportException if there was a transport-layer error * @throws TransportException if there was a transport-layer error
*/ */
public void authPublickey(String username, String... locations) throws UserAuthException, TransportException { public void authPublickey(String username, String... locations)
throws UserAuthException, TransportException {
List<KeyProvider> keyProviders = new LinkedList<KeyProvider>(); List<KeyProvider> keyProviders = new LinkedList<KeyProvider>();
for (String loc : locations) for (String loc : locations)
try { try {
@@ -349,7 +359,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* cleanup is done and the thread spawned by the transport layer for dealing with incoming packets is stopped. * cleanup is done and the thread spawned by the transport layer for dealing with incoming packets is stopped.
*/ */
@Override @Override
public void disconnect() throws IOException { public void disconnect()
throws IOException {
assert isConnected(); assert isConnected();
trans.disconnect(); trans.disconnect();
super.disconnect(); super.disconnect();
@@ -411,7 +422,7 @@ public class SSHClient extends SocketClient implements SessionFactory {
/** /**
* Returns a {@link KeyProvider} instance created from a location on the file system where an <em>unencrypted</em> * Returns a {@link KeyProvider} instance created from a location on the file system where an <em>unencrypted</em>
* private key file (does not require a passphrase) can be found. Simply calls {@link #loadKeys(String, * private key file (does not require a passphrase) can be found. Simply calls {@link #loadKeys(String,
* PasswordFinder)} with the {@link net.schmizz.sshj.userauth.password.PasswordFinder} argument as {@code null}. * PasswordFinder)} with the {@link PasswordFinder} argument as {@code null}.
* *
* @param location the location for the key file * @param location the location for the key file
* *
@@ -421,26 +432,27 @@ public class SSHClient extends SocketClient implements SessionFactory {
* BouncyCastle is not in the classpath * BouncyCastle is not in the classpath
* @throws IOException if the key file format is not known, if the file could not be read, etc. * @throws IOException if the key file format is not known, if the file could not be read, etc.
*/ */
public KeyProvider loadKeys(String location) throws IOException { public KeyProvider loadKeys(String location)
throws IOException {
return loadKeys(location, (PasswordFinder) null); return loadKeys(location, (PasswordFinder) null);
} }
/** /**
* Utility function for createing a {@link KeyProvider} instance from given location on the file system. Creates a * Utility function for createing a {@link KeyProvider} instance from given location on the file system. Creates a
* one-off {@link PasswordFinder} using {@link net.schmizz.sshj.userauth.password.PasswordUtils#createOneOff(char[])}, * one-off {@link PasswordFinder} using {@link PasswordUtils#createOneOff(char[])}, and calls {@link
* and calls {@link #loadKeys(String,PasswordFinder)}. * #loadKeys(String,PasswordFinder)}.
* *
* @param location location of the key file * @param location location of the key file
* @param passphrase passphrase as a char-array * @param passphrase passphrase as a char-array
* *
* @return the key provider ready for use in authentication * @return the key provider ready for use in authentication
* *
* @throws net.schmizz.sshj.common.SSHException * @throws SSHException if there was no suitable key provider available for the file format; typically because
* if there was no suitable key provider available for the file format; typically because
* BouncyCastle is not in the classpath * BouncyCastle is not in the classpath
* @throws IOException if the key file format is not known, if the file could not be read, etc. * @throws IOException if the key file format is not known, if the file could not be read, etc.
*/ */
public KeyProvider loadKeys(String location, char[] passphrase) throws IOException { public KeyProvider loadKeys(String location, char[] passphrase)
throws IOException {
return loadKeys(location, PasswordUtils.createOneOff(passphrase)); return loadKeys(location, PasswordUtils.createOneOff(passphrase));
} }
@@ -459,7 +471,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* BouncyCastle is not in the classpath * BouncyCastle is not in the classpath
* @throws IOException if the key file format is not known, if the file could not be read, etc. * @throws IOException if the key file format is not known, if the file could not be read, etc.
*/ */
public KeyProvider loadKeys(String location, PasswordFinder passwordFinder) throws IOException { public KeyProvider loadKeys(String location, PasswordFinder passwordFinder)
throws IOException {
File loc = new File(location); File loc = new File(location);
FileKeyProvider.Format format = KeyProviderUtil.detectKeyFileFormat(loc); FileKeyProvider.Format format = KeyProviderUtil.detectKeyFileFormat(loc);
FileKeyProvider fkp = Factory.Named.Util.create(trans.getConfig().getFileKeyProviderFactories(), format FileKeyProvider fkp = Factory.Named.Util.create(trans.getConfig().getFileKeyProviderFactories(), format
@@ -482,7 +495,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* *
* @throws IOException if the key file format is not known, if the file could not be read etc. * @throws IOException if the key file format is not known, if the file could not be read etc.
*/ */
public KeyProvider loadKeys(String location, String passphrase) throws IOException { public KeyProvider loadKeys(String location, String passphrase)
throws IOException {
return loadKeys(location, passphrase.toCharArray()); return loadKeys(location, passphrase.toCharArray());
} }
@@ -495,7 +509,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* *
* @throws IOException if there is an error loading from <em>both</em> locations * @throws IOException if there is an error loading from <em>both</em> locations
*/ */
public void loadKnownHosts() throws IOException { public void loadKnownHosts()
throws IOException {
boolean loaded = false; boolean loaded = false;
final File sshDir = OpenSSHKnownHosts.detectSSHDir(); final File sshDir = OpenSSHKnownHosts.detectSSHDir();
if (sshDir != null) { if (sshDir != null) {
@@ -515,7 +530,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* *
* @throws IOException if there is an error loading from any of these locations * @throws IOException if there is an error loading from any of these locations
*/ */
public void loadKnownHosts(File location) throws IOException { public void loadKnownHosts(File location)
throws IOException {
addHostKeyVerifier(new OpenSSHKnownHosts(location)); addHostKeyVerifier(new OpenSSHKnownHosts(location));
} }
@@ -526,7 +542,7 @@ public class SSHClient extends SocketClient implements SessionFactory {
* The returned forwarder's {@link LocalPortForwarder#listen() listen()} method should be called to actually start * The returned forwarder's {@link LocalPortForwarder#listen() listen()} method should be called to actually start
* listening, this method just creates an instance. * listening, this method just creates an instance.
* *
* @param address defines where the {@link net.schmizz.sshj.connection.channel.direct.LocalPortForwarder} listens * @param address defines where the {@link LocalPortForwarder} listens
* @param host hostname to which the server will forward * @param host hostname to which the server will forward
* @param port the port at {@code hostname} to which the server wil forward * @param port the port at {@code hostname} to which the server wil forward
* *
@@ -534,7 +550,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* *
* @throws IOException if there is an error opening a local server socket * @throws IOException if there is an error opening a local server socket
*/ */
public LocalPortForwarder newLocalPortForwarder(SocketAddress address, String host, int port) throws IOException { public LocalPortForwarder newLocalPortForwarder(SocketAddress address, String host, int port)
throws IOException {
return new LocalPortForwarder(getServerSocketFactory(), conn, address, host, port); return new LocalPortForwarder(getServerSocketFactory(), conn, address, host, port);
} }
@@ -549,8 +566,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @param listener the {@link ConnectListener} that should be delegated the responsibility of handling forwarded * @param listener the {@link ConnectListener} that should be delegated the responsibility of handling forwarded
* {@link X11Channel} 's * {@link X11Channel} 's
* *
* @return an {@link net.schmizz.sshj.connection.channel.forwarded.X11Forwarder} that allows to {@link * @return an {@link X11Forwarder} that allows to {@link X11Forwarder#stop() stop acting} on X11 requests from
* X11Forwarder#stop() stop acting} on X11 requests from server * server
*/ */
public X11Forwarder registerX11Forwarder(ConnectListener listener) { public X11Forwarder registerX11Forwarder(ConnectListener listener) {
X11Forwarder x11f = new X11Forwarder(conn, listener); X11Forwarder x11f = new X11Forwarder(conn, listener);
@@ -570,7 +587,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @throws IOException if there is an error starting the {@code sftp} subsystem * @throws IOException if there is an error starting the {@code sftp} subsystem
* @see StatefulSFTPClient * @see StatefulSFTPClient
*/ */
public SFTPClient newSFTPClient() throws IOException { public SFTPClient newSFTPClient()
throws IOException {
assert isConnected() && isAuthenticated(); assert isConnected() && isAuthenticated();
return new SFTPClient(this); return new SFTPClient(this);
} }
@@ -580,11 +598,13 @@ public class SSHClient extends SocketClient implements SessionFactory {
* *
* @throws TransportException if an error occurs during key exchange * @throws TransportException if an error occurs during key exchange
*/ */
public void rekey() throws TransportException { public void rekey()
throws TransportException {
doKex(); doKex();
} }
public Session startSession() throws ConnectionException, TransportException { public Session startSession()
throws ConnectionException, TransportException {
assert isConnected() && isAuthenticated(); assert isConnected() && isAuthenticated();
SessionChannel sess = new SessionChannel(conn); SessionChannel sess = new SessionChannel(conn);
sess.open(); sess.open();
@@ -602,7 +622,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* @throws ClassNotFoundException if {@code JZlib} is not in classpath * @throws ClassNotFoundException if {@code JZlib} is not in classpath
* @throws TransportException if an error occurs during renegotiation * @throws TransportException if an error occurs during renegotiation
*/ */
public void useCompression() throws ClassNotFoundException, TransportException { public void useCompression()
throws ClassNotFoundException, TransportException {
trans.getConfig().setCompressionFactories(Arrays.asList( trans.getConfig().setCompressionFactories(Arrays.asList(
new DelayedZlibCompression.Factory(), new DelayedZlibCompression.Factory(),
new ZlibCompression.Factory(), new ZlibCompression.Factory(),
@@ -613,7 +634,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
/** On connection establishment, also initialize the SSH transport via {@link Transport#init} and {@link #doKex()}. */ /** On connection establishment, also initialize the SSH transport via {@link Transport#init} and {@link #doKex()}. */
@Override @Override
protected void onConnect() throws IOException { protected void onConnect()
throws IOException {
super.onConnect(); super.onConnect();
trans.init(getRemoteHostname(), getRemotePort(), getInputStream(), getOutputStream()); trans.init(getRemoteHostname(), getRemotePort(), getInputStream(), getOutputStream());
doKex(); doKex();
@@ -624,7 +646,8 @@ public class SSHClient extends SocketClient implements SessionFactory {
* *
* @throws TransportException if error during kex * @throws TransportException if error during kex
*/ */
protected void doKex() throws TransportException { protected void doKex()
throws TransportException {
assert trans.isRunning(); assert trans.isRunning();
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();

View File

@@ -21,7 +21,8 @@ import net.schmizz.sshj.common.SSHPacketHandler;
import net.schmizz.sshj.transport.TransportException; import net.schmizz.sshj.transport.TransportException;
/** Represents a service running on top of the SSH {@link net.schmizz.sshj.transport.Transport transport layer}. */ /** Represents a service running on top of the SSH {@link net.schmizz.sshj.transport.Transport transport layer}. */
public interface Service extends SSHPacketHandler, ErrorNotifiable { public interface Service
extends SSHPacketHandler, ErrorNotifiable {
/** @return The assigned name for this SSH service. */ /** @return The assigned name for this SSH service. */
String getName(); String getName();
@@ -34,17 +35,20 @@ public interface Service extends SSHPacketHandler, ErrorNotifiable {
* *
* @throws SSHException if the packet is unexpected and may represent a disruption * @throws SSHException if the packet is unexpected and may represent a disruption
*/ */
void notifyUnimplemented(long seqNum) throws SSHException; void notifyUnimplemented(long seqNum)
throws SSHException;
/** /**
* Request and install this service with the associated transport. Implementations should aim to make this method * Request and install this service with the associated transport. Implementations should aim to make this method
* idempotent by first checking the {@link net.schmizz.sshj.transport.Transport#getService() currently active * idempotent by first checking the {@link net.schmizz.sshj.transport.Transport#getService()} currently active
* service}. * service}.
* *
* @throws TransportException if there is an error sending the service request * @throws TransportException if there is an error sending the service request
*/ */
void request() throws TransportException; void request()
throws TransportException;
void notifyDisconnect() throws SSHException; void notifyDisconnect()
throws SSHException;
} }

View File

@@ -48,12 +48,33 @@ import java.util.Arrays;
public class Buffer<T extends Buffer<T>> { public class Buffer<T extends Buffer<T>> {
public static class BufferException extends SSHRuntimeException { public static class BufferException
extends SSHRuntimeException {
public BufferException(String message) { public BufferException(String message) {
super(message); super(message);
} }
} }
public static class PlainBuffer
extends Buffer<PlainBuffer> {
public PlainBuffer() {
super();
}
public PlainBuffer(Buffer<?> from) {
super(from);
}
public PlainBuffer(byte[] b) {
super(b);
}
public PlainBuffer(int size) {
super(size);
}
}
/** The default size for a {@code Buffer} (256 bytes) */ /** The default size for a {@code Buffer} (256 bytes) */
public static final int DEFAULT_SIZE = 256; public static final int DEFAULT_SIZE = 256;
@@ -473,18 +494,22 @@ public class Buffer<T extends Buffer<T>> {
public T putPublicKey(PublicKey key) { public T putPublicKey(PublicKey key) {
KeyType type = KeyType.fromKey(key); KeyType type = KeyType.fromKey(key);
switch (type) { switch (type) {
case RSA: case RSA: {
final RSAPublicKey rsaKey = (RSAPublicKey) key;
putString(type.toString()) // ssh-rsa putString(type.toString()) // ssh-rsa
.putMPInt(((RSAPublicKey) key).getPublicExponent()) // e .putMPInt(rsaKey.getPublicExponent()) // e
.putMPInt(((RSAPublicKey) key).getModulus()); // n .putMPInt(rsaKey.getModulus()); // n
break; break;
case DSA: }
case DSA: {
final DSAPublicKey dsaKey = (DSAPublicKey) key;
putString(type.toString()) // ssh-dss putString(type.toString()) // ssh-dss
.putMPInt(((DSAPublicKey) key).getParams().getP()) // p .putMPInt(dsaKey.getParams().getP()) // p
.putMPInt(((DSAPublicKey) key).getParams().getQ()) // q .putMPInt(dsaKey.getParams().getQ()) // q
.putMPInt(((DSAPublicKey) key).getParams().getG()) // g .putMPInt(dsaKey.getParams().getG()) // g
.putMPInt(((DSAPublicKey) key).getY()); // y .putMPInt(dsaKey.getY()); // y
break; break;
}
default: default:
assert false; assert false;
} }
@@ -492,7 +517,8 @@ public class Buffer<T extends Buffer<T>> {
} }
public T putSignature(String sigFormat, byte[] sigData) { public T putSignature(String sigFormat, byte[] sigData) {
return putString(new PlainBuffer().putString(sigFormat).putBytes(sigData).getCompactData()); final byte[] sig = new PlainBuffer().putString(sigFormat).putBytes(sigData).getCompactData();
return putString(sig);
} }
/** /**
@@ -509,23 +535,4 @@ public class Buffer<T extends Buffer<T>> {
return "Buffer [rpos=" + rpos + ", wpos=" + wpos + ", size=" + data.length + "]"; return "Buffer [rpos=" + rpos + ", wpos=" + wpos + ", size=" + data.length + "]";
} }
public static class PlainBuffer extends Buffer<PlainBuffer> {
public PlainBuffer() {
super();
}
public PlainBuffer(Buffer<?> from) {
super(from);
}
public PlainBuffer(byte[] b) {
super(b);
}
public PlainBuffer(int size) {
super(size);
}
}
} }

View File

@@ -51,7 +51,8 @@ public interface Factory<T> {
* *
* @param <T> type of object created by this factory * @param <T> type of object created by this factory
*/ */
interface Named<T> extends Factory<T> { interface Named<T>
extends Factory<T> {
/** Utility functions */ /** Utility functions */
public static class Util { public static class Util {

View File

@@ -43,7 +43,8 @@ import java.io.IOException;
* Most exceptions in {@code org.apache.commons.net.ssh} are instances of this class. An {@link SSHException} is itself * Most exceptions in {@code org.apache.commons.net.ssh} are instances of this class. An {@link SSHException} is itself
* an {@link IOException} and can be caught like that if this level of granularity is not desired. * an {@link IOException} and can be caught like that if this level of granularity is not desired.
*/ */
public class SSHException extends IOException { public class SSHException
extends IOException {
public static final ExceptionChainer<SSHException> chainer = new ExceptionChainer<SSHException>() { public static final ExceptionChainer<SSHException> chainer = new ExceptionChainer<SSHException>() {

View File

@@ -37,7 +37,8 @@ package net.schmizz.sshj.common;
import java.util.Arrays; import java.util.Arrays;
public class SSHPacket extends Buffer<SSHPacket> { public class SSHPacket
extends Buffer<SSHPacket> {
public SSHPacket() { public SSHPacket() {
super(); super();

View File

@@ -30,6 +30,7 @@ public interface SSHPacketHandler {
* *
* @throws SSHException if there is a non-recoverable error * @throws SSHException if there is a non-recoverable error
*/ */
void handle(Message msg, SSHPacket buf) throws SSHException; void handle(Message msg, SSHPacket buf)
throws SSHException;
} }

View File

@@ -36,7 +36,8 @@
package net.schmizz.sshj.common; package net.schmizz.sshj.common;
/** Represents unrecoverable exceptions in the {@code org.apache.commons.net.ssh} package. */ /** Represents unrecoverable exceptions in the {@code org.apache.commons.net.ssh} package. */
public class SSHRuntimeException extends RuntimeException { public class SSHRuntimeException
extends RuntimeException {
public SSHRuntimeException() { public SSHRuntimeException() {
this(null, null); this(null, null);

View File

@@ -52,11 +52,14 @@ import java.security.NoSuchProviderException;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.Signature; import java.security.Signature;
// TODO refactor
/** Static utility method relating to security facilities. */ /** Static utility method relating to security facilities. */
public class SecurityUtils { public class SecurityUtils {
private static class BouncyCastleRegistration { private static class BouncyCastleRegistration {
public void run() throws Exception { public void run()
throws Exception {
if (java.security.Security.getProvider(BOUNCY_CASTLE) == null) { if (java.security.Security.getProvider(BOUNCY_CASTLE) == null) {
LOG.info("Trying to register BouncyCastle as a JCE provider"); LOG.info("Trying to register BouncyCastle as a JCE provider");
java.security.Security.addProvider(new BouncyCastleProvider()); java.security.Security.addProvider(new BouncyCastleProvider());
@@ -83,8 +86,8 @@ public class SecurityUtils {
private static Boolean registerBouncyCastle; private static Boolean registerBouncyCastle;
private static boolean registrationDone; private static boolean registrationDone;
public static synchronized Cipher getCipher(String transformation) throws NoSuchAlgorithmException, public static synchronized Cipher getCipher(String transformation)
NoSuchPaddingException, NoSuchProviderException { throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
register(); register();
if (getSecurityProvider() == null) if (getSecurityProvider() == null)
return Cipher.getInstance(transformation); return Cipher.getInstance(transformation);
@@ -127,8 +130,8 @@ public class SecurityUtils {
* @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException
* @throws NoSuchProviderException * @throws NoSuchProviderException
*/ */
public static synchronized KeyAgreement getKeyAgreement(String algorithm) throws NoSuchAlgorithmException, public static synchronized KeyAgreement getKeyAgreement(String algorithm)
NoSuchProviderException { throws NoSuchAlgorithmException, NoSuchProviderException {
register(); register();
if (getSecurityProvider() == null) if (getSecurityProvider() == null)
return KeyAgreement.getInstance(algorithm); return KeyAgreement.getInstance(algorithm);
@@ -146,8 +149,8 @@ public class SecurityUtils {
* @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException
* @throws NoSuchProviderException * @throws NoSuchProviderException
*/ */
public static synchronized KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException, public static synchronized KeyFactory getKeyFactory(String algorithm)
NoSuchProviderException { throws NoSuchAlgorithmException, NoSuchProviderException {
register(); register();
if (getSecurityProvider() == null) if (getSecurityProvider() == null)
return KeyFactory.getInstance(algorithm); return KeyFactory.getInstance(algorithm);
@@ -165,8 +168,8 @@ public class SecurityUtils {
* @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException
* @throws NoSuchProviderException * @throws NoSuchProviderException
*/ */
public static synchronized KeyPairGenerator getKeyPairGenerator(String algorithm) throws NoSuchAlgorithmException, public static synchronized KeyPairGenerator getKeyPairGenerator(String algorithm)
NoSuchProviderException { throws NoSuchAlgorithmException, NoSuchProviderException {
register(); register();
if (getSecurityProvider() == null) if (getSecurityProvider() == null)
return KeyPairGenerator.getInstance(algorithm); return KeyPairGenerator.getInstance(algorithm);
@@ -184,7 +187,8 @@ public class SecurityUtils {
* @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException
* @throws NoSuchProviderException * @throws NoSuchProviderException
*/ */
public static synchronized Mac getMAC(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException { public static synchronized Mac getMAC(String algorithm)
throws NoSuchAlgorithmException, NoSuchProviderException {
register(); register();
if (getSecurityProvider() == null) if (getSecurityProvider() == null)
return Mac.getInstance(algorithm); return Mac.getInstance(algorithm);
@@ -202,8 +206,8 @@ public class SecurityUtils {
* @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException
* @throws NoSuchProviderException * @throws NoSuchProviderException
*/ */
public static synchronized MessageDigest getMessageDigest(String algorithm) throws NoSuchAlgorithmException, public static synchronized MessageDigest getMessageDigest(String algorithm)
NoSuchProviderException { throws NoSuchAlgorithmException, NoSuchProviderException {
register(); register();
if (getSecurityProvider() == null) if (getSecurityProvider() == null)
return MessageDigest.getInstance(algorithm); return MessageDigest.getInstance(algorithm);
@@ -221,8 +225,8 @@ public class SecurityUtils {
return securityProvider; return securityProvider;
} }
public static synchronized Signature getSignature(String algorithm) throws NoSuchAlgorithmException, public static synchronized Signature getSignature(String algorithm)
NoSuchProviderException { throws NoSuchAlgorithmException, NoSuchProviderException {
register(); register();
if (getSecurityProvider() == null) if (getSecurityProvider() == null)
return Signature.getInstance(algorithm); return Signature.getInstance(algorithm);

View File

@@ -24,7 +24,8 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays; import java.util.Arrays;
public class StreamCopier extends Thread { public class StreamCopier
extends Thread {
private static final Logger LOG = LoggerFactory.getLogger(StreamCopier.class); private static final Logger LOG = LoggerFactory.getLogger(StreamCopier.class);
@@ -41,7 +42,8 @@ public class StreamCopier extends Thread {
}; };
} }
public static long copy(InputStream in, OutputStream out, int bufSize, boolean keepFlushing) throws IOException { public static long copy(InputStream in, OutputStream out, int bufSize, boolean keepFlushing)
throws IOException {
long count = 0; long count = 0;
final long startTime = System.currentTimeMillis(); final long startTime = System.currentTimeMillis();
@@ -64,7 +66,8 @@ public class StreamCopier extends Thread {
return count; return count;
} }
public static String copyStreamToString(InputStream stream) throws IOException { public static String copyStreamToString(InputStream stream)
throws IOException {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
int read; int read;
while ((read = stream.read()) != -1) while ((read = stream.read()) != -1)

View File

@@ -12,26 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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; package net.schmizz.sshj.connection;
@@ -43,16 +23,14 @@ import net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener;
import net.schmizz.sshj.transport.Transport; import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException; import net.schmizz.sshj.transport.TransportException;
/** /** Connection layer of the SSH protocol. Refer to RFC 254. */
* Connection layer of the SSH protocol.
*
* @see rfc4254
*/
public interface Connection { public interface Connection {
/** /**
* Attach a {@link net.schmizz.sshj.connection.channel.Channel} to this connection. A channel must be attached to * Attach a {@link net.schmizz.sshj.connection.channel.Channel} to this connection. A channel must be attached to
* the connection if it is to receive any channel-specific data that is received. * the connection if it is to receive any channel-specific data that is received.
*
* @param chan
*/ */
void attach(Channel chan); void attach(Channel chan);
@@ -60,24 +38,45 @@ public interface Connection {
* Attach a {@link net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener} to this connection, which * Attach a {@link net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener} to this connection, which
* will be delegated opening of any {@code CHANNEL_OPEN} packets {@link net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener#getChannelType() * will be delegated opening of any {@code CHANNEL_OPEN} packets {@link net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener#getChannelType()
* for which it is responsible}. * for which it is responsible}.
*
* @param opener
*/ */
void attach(ForwardedChannelOpener opener); void attach(ForwardedChannelOpener opener);
/** Forget an attached {@link Channel}. */ /**
* Forget an attached {@link Channel}.
*
* @param chan
*/
void forget(Channel chan); void forget(Channel chan);
/** Forget an attached {@link net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener}. */ /**
* Forget an attached {@link net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener}.
*
* @param handler
*/
void forget(ForwardedChannelOpener handler); void forget(ForwardedChannelOpener handler);
/** Returns an attached {@link Channel} of specified channel-id, or {@code null} if no such channel was attached */ /**
* Returns an attached {@link Channel} of specified channel-id, or {@code null} if no such channel was attached
*
* @param id
*/
Channel get(int id); Channel get(int id);
/** Wait for the situation that no channels are attached (e.g., got closed). */ /**
void join() throws InterruptedException; * Wait for the situation that no channels are attached (e.g., got closed).
*
* @throws InterruptedException
*/
void join()
throws InterruptedException;
/** /**
* Returns an attached {@link net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener} of specified * @param chanType channel type
* channel-type, or {@code null} if no such channel was attached *
* @return an attached {@link ForwardedChannelOpener} of specified channel-type, or {@code null} if no such channel
* was attached
*/ */
ForwardedChannelOpener get(String chanType); ForwardedChannelOpener get(String chanType);
@@ -97,7 +96,8 @@ public interface Connection {
* @throws TransportException if there is an error sending the request * @throws TransportException if there is an error sending the request
*/ */
public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply, public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
byte[] specifics) throws TransportException; byte[] specifics)
throws TransportException;
/** /**
* Send a {@code SSH_MSG_OPEN_FAILURE} for specified {@code Reason} and {@code message}. * Send a {@code SSH_MSG_OPEN_FAILURE} for specified {@code Reason} and {@code message}.
@@ -108,7 +108,8 @@ public interface Connection {
* *
* @throws TransportException * @throws TransportException
*/ */
void sendOpenFailure(int recipient, OpenFailException.Reason reason, String message) throws TransportException; void sendOpenFailure(int recipient, OpenFailException.Reason reason, String message)
throws TransportException;
/** /**
* Get the maximum packet size for the local window this connection recommends to any {@link Channel}'s that ask for * Get the maximum packet size for the local window this connection recommends to any {@link Channel}'s that ask for

View File

@@ -12,26 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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; package net.schmizz.sshj.connection;
@@ -40,7 +20,8 @@ import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.SSHException; import net.schmizz.sshj.common.SSHException;
/** Connection-layer exception. */ /** Connection-layer exception. */
public class ConnectionException extends SSHException { public class ConnectionException
extends SSHException {
public static final ExceptionChainer<ConnectionException> chainer = new ExceptionChainer<ConnectionException>() { public static final ExceptionChainer<ConnectionException> chainer = new ExceptionChainer<ConnectionException>() {
public ConnectionException chain(Throwable t) { public ConnectionException chain(Throwable t) {

View File

@@ -37,7 +37,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** {@link Connection} implementation. */ /** {@link Connection} implementation. */
public class ConnectionImpl extends AbstractService implements Connection { public class ConnectionImpl
extends AbstractService
implements Connection {
private final Object internalSynchronizer = new Object(); private final Object internalSynchronizer = new Object();
@@ -93,7 +95,8 @@ public class ConnectionImpl extends AbstractService implements Connection {
openers.put(opener.getChannelType(), opener); openers.put(opener.getChannelType(), opener);
} }
private Channel getChannel(SSHPacket buffer) throws ConnectionException { private Channel getChannel(SSHPacket buffer)
throws ConnectionException {
int recipient = buffer.readInt(); int recipient = buffer.readInt();
Channel channel = get(recipient); Channel channel = get(recipient);
if (channel != null) if (channel != null)
@@ -106,7 +109,8 @@ public class ConnectionImpl extends AbstractService implements Connection {
} }
@Override @Override
public void handle(Message msg, SSHPacket buf) throws SSHException { public void handle(Message msg, SSHPacket buf)
throws SSHException {
if (msg.in(91, 100)) if (msg.in(91, 100))
getChannel(buf).handle(msg, buf); getChannel(buf).handle(msg, buf);
@@ -162,7 +166,8 @@ public class ConnectionImpl extends AbstractService implements Connection {
this.windowSize = windowSize; this.windowSize = windowSize;
} }
public void join() throws InterruptedException { public void join()
throws InterruptedException {
synchronized (internalSynchronizer) { synchronized (internalSynchronizer) {
while (!channels.isEmpty()) while (!channels.isEmpty())
internalSynchronizer.wait(); internalSynchronizer.wait();
@@ -174,10 +179,12 @@ public class ConnectionImpl extends AbstractService implements Connection {
} }
public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply, public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
byte[] specifics) throws TransportException { byte[] specifics)
throws TransportException {
synchronized (globalReqFutures) { synchronized (globalReqFutures) {
log.info("Making global request for `{}`", name); log.info("Making global request for `{}`", name);
trans.write(new SSHPacket(Message.GLOBAL_REQUEST).putString(name).putBoolean(wantReply).putRawBytes(specifics)); trans.write(new SSHPacket(Message.GLOBAL_REQUEST).putString(name)
.putBoolean(wantReply).putRawBytes(specifics));
Future<SSHPacket, ConnectionException> future = null; Future<SSHPacket, ConnectionException> future = null;
if (wantReply) { if (wantReply) {
@@ -188,7 +195,8 @@ public class ConnectionImpl extends AbstractService implements Connection {
} }
} }
private void gotGlobalReqResponse(SSHPacket response) throws ConnectionException { private void gotGlobalReqResponse(SSHPacket response)
throws ConnectionException {
synchronized (globalReqFutures) { synchronized (globalReqFutures) {
Future<SSHPacket, ConnectionException> gr = globalReqFutures.poll(); Future<SSHPacket, ConnectionException> gr = globalReqFutures.poll();
if (gr == null) if (gr == null)
@@ -201,7 +209,8 @@ public class ConnectionImpl extends AbstractService implements Connection {
} }
} }
private void gotChannelOpen(SSHPacket buf) throws ConnectionException, TransportException { private void gotChannelOpen(SSHPacket buf)
throws ConnectionException, TransportException {
final String type = buf.readString(); final String type = buf.readString();
log.debug("Received CHANNEL_OPEN for `{}` channel", type); log.debug("Received CHANNEL_OPEN for `{}` channel", type);
if (openers.containsKey(type)) if (openers.containsKey(type))
@@ -212,17 +221,19 @@ public class ConnectionImpl extends AbstractService implements Connection {
} }
} }
public void sendOpenFailure(int recipient, Reason reason, String message) throws TransportException { public void sendOpenFailure(int recipient, Reason reason, String message)
trans.write(new SSHPacket(Message.CHANNEL_OPEN_FAILURE) // throws TransportException {
.putInt(recipient) // trans.write(new SSHPacket(Message.CHANNEL_OPEN_FAILURE)
.putInt(reason.getCode()) // .putInt(recipient)
.putInt(reason.getCode())
.putString(message)); .putString(message));
} }
@Override @Override
public void notifyDisconnect() throws SSHException { public void notifyDisconnect()
throws SSHException {
super.notifyDisconnect(); super.notifyDisconnect();
// wh'about them futures? FutureUtils.alertAll(new ConnectionException("Disconnected."), globalReqFutures);
for (Channel chan : channels.values()) for (Channel chan : channels.values())
chan.close(); chan.close();
} }

View File

@@ -58,7 +58,8 @@ import java.util.Queue;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
public abstract class AbstractChannel implements Channel { public abstract class AbstractChannel
implements Channel {
/** Logger */ /** Logger */
protected final Logger log; protected final Logger log;
@@ -164,7 +165,8 @@ public abstract class AbstractChannel implements Channel {
return type; return type;
} }
public void handle(Message msg, SSHPacket buf) throws ConnectionException, TransportException { public void handle(Message msg, SSHPacket buf)
throws ConnectionException, TransportException {
switch (msg) { switch (msg) {
case CHANNEL_DATA: case CHANNEL_DATA:
@@ -205,7 +207,8 @@ public abstract class AbstractChannel implements Channel {
} }
} }
private void gotClose() throws TransportException { private void gotClose()
throws TransportException {
log.info("Got close"); log.info("Got close");
try { try {
closeAllStreams(); closeAllStreams();
@@ -236,7 +239,8 @@ public abstract class AbstractChannel implements Channel {
this.autoExpand = autoExpand; this.autoExpand = autoExpand;
} }
public void close() throws ConnectionException, TransportException { public void close()
throws ConnectionException, TransportException {
lock.lock(); lock.lock();
try { try {
try { try {
@@ -251,7 +255,8 @@ public abstract class AbstractChannel implements Channel {
} }
} }
protected synchronized void sendClose() throws TransportException { protected synchronized void sendClose()
throws TransportException {
try { try {
if (!closeRequested) { if (!closeRequested) {
log.info("Sending close"); log.info("Sending close");
@@ -271,7 +276,8 @@ public abstract class AbstractChannel implements Channel {
} }
} }
private void gotChannelRequest(SSHPacket buf) throws ConnectionException, TransportException { private void gotChannelRequest(SSHPacket buf)
throws ConnectionException, TransportException {
final String reqType = buf.readString(); final String reqType = buf.readString();
buf.readBoolean(); // We don't care about the 'want-reply' value buf.readBoolean(); // We don't care about the 'want-reply' value
log.info("Got chan request for `{}`", reqType); log.info("Got chan request for `{}`", reqType);
@@ -289,15 +295,18 @@ public abstract class AbstractChannel implements Channel {
close.set(); close.set();
} }
protected void gotExtendedData(int dataTypeCode, SSHPacket buf) throws ConnectionException, TransportException { protected void gotExtendedData(int dataTypeCode, SSHPacket buf)
throws ConnectionException, TransportException {
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Extended data not supported on " + type throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Extended data not supported on " + type
+ " channel"); + " channel");
} }
protected void gotUnknown(Message msg, SSHPacket buf) throws ConnectionException, TransportException { protected void gotUnknown(Message msg, SSHPacket buf)
throws ConnectionException, TransportException {
} }
protected void handleRequest(String reqType, SSHPacket buf) throws ConnectionException, TransportException { protected void handleRequest(String reqType, SSHPacket buf)
throws ConnectionException, TransportException {
trans.write(newBuffer(Message.CHANNEL_FAILURE)); trans.write(newBuffer(Message.CHANNEL_FAILURE));
} }
@@ -305,7 +314,8 @@ public abstract class AbstractChannel implements Channel {
return new SSHPacket(cmd).putInt(recipient); return new SSHPacket(cmd).putInt(recipient);
} }
protected void receiveInto(ChannelInputStream stream, SSHPacket buf) throws ConnectionException, TransportException { protected void receiveInto(ChannelInputStream stream, SSHPacket buf)
throws ConnectionException, TransportException {
final int len = buf.readInt(); final int len = buf.readInt();
if (len < 0 || len > getLocalMaxPacketSize() || len != buf.available()) if (len < 0 || len > getLocalMaxPacketSize() || len != buf.available())
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Bad item length: " + len); throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Bad item length: " + len);
@@ -315,7 +325,8 @@ public abstract class AbstractChannel implements Channel {
} }
protected synchronized Event<ConnectionException> sendChannelRequest(String reqType, boolean wantReply, protected synchronized Event<ConnectionException> sendChannelRequest(String reqType, boolean wantReply,
Buffer.PlainBuffer reqSpecific) throws TransportException { Buffer.PlainBuffer reqSpecific)
throws TransportException {
log.info("Sending channel request for `{}`", reqType); log.info("Sending channel request for `{}`", reqType);
trans.write( trans.write(
newBuffer(Message.CHANNEL_REQUEST) newBuffer(Message.CHANNEL_REQUEST)
@@ -332,7 +343,8 @@ public abstract class AbstractChannel implements Channel {
return responseEvent; return responseEvent;
} }
private synchronized void gotResponse(boolean success) throws ConnectionException { private synchronized void gotResponse(boolean success)
throws ConnectionException {
final Event<ConnectionException> responseEvent = chanReqResponseEvents.poll(); final Event<ConnectionException> responseEvent = chanReqResponseEvents.poll();
if (responseEvent != null) { if (responseEvent != null) {
if (success) if (success)
@@ -345,7 +357,8 @@ public abstract class AbstractChannel implements Channel {
"Received response to channel request when none was requested"); "Received response to channel request when none was requested");
} }
private synchronized void gotEOF() throws TransportException { private synchronized void gotEOF()
throws TransportException {
log.info("Got EOF"); log.info("Got EOF");
eofGot = true; eofGot = true;
eofInputStreams(); eofInputStreams();
@@ -358,7 +371,8 @@ public abstract class AbstractChannel implements Channel {
in.eof(); in.eof();
} }
public synchronized void sendEOF() throws TransportException { public synchronized void sendEOF()
throws TransportException {
try { try {
if (!closeRequested && !eofSent) { if (!closeRequested && !eofSent) {
log.info("Sending EOF"); log.info("Sending EOF");

View File

@@ -12,26 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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; package net.schmizz.sshj.connection.channel;
@@ -45,10 +25,12 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
/** A channel is the basic medium for application-layer data on top of an SSH transport. */ /** A channel is the basic medium for application-layer data on top of an SSH transport. */
public interface Channel extends Closeable, SSHPacketHandler, ErrorNotifiable { public interface Channel
extends Closeable, SSHPacketHandler, ErrorNotifiable {
/** Direct channels are those that are initiated by us. */ /** Direct channels are those that are initiated by us. */
interface Direct extends Channel { interface Direct
extends Channel {
/** /**
* Request opening this channel from remote end. * Request opening this channel from remote end.
* *
@@ -57,19 +39,22 @@ public interface Channel extends Closeable, SSHPacketHandler, ErrorNotifiable {
* other connection-layer error * other connection-layer error
* @throws TransportException error writing packets etc. * @throws TransportException error writing packets etc.
*/ */
void open() throws OpenFailException, ConnectionException, TransportException; void open()
throws OpenFailException, ConnectionException, TransportException;
} }
/** Forwarded channels are those that are initiated by the server. */ /** Forwarded channels are those that are initiated by the server. */
interface Forwarded extends Channel { interface Forwarded
extends Channel {
/** /**
* Confirm {@code CHANNEL_OPEN} request. * Confirm {@code CHANNEL_OPEN} request.
* *
* @throws TransportException error sending confirmation packet * @throws TransportException error sending confirmation packet
*/ */
void confirm() throws TransportException; void confirm()
throws TransportException;
/** Returns the IP of where the forwarded connection originates. */ /** Returns the IP of where the forwarded connection originates. */
String getOriginatorIP(); String getOriginatorIP();
@@ -85,13 +70,15 @@ public interface Channel extends Closeable, SSHPacketHandler, ErrorNotifiable {
* *
* @throws TransportException error sending rejection packet * @throws TransportException error sending rejection packet
*/ */
void reject(OpenFailException.Reason reason, String message) throws TransportException; void reject(OpenFailException.Reason reason, String message)
throws TransportException;
} }
/** Close this channel. */ /** Close this channel. */
void close() throws TransportException, ConnectionException; void close()
throws TransportException, ConnectionException;
/** /**
* Returns whether auto-expansion of local window is set. * Returns whether auto-expansion of local window is set.
@@ -103,38 +90,41 @@ public interface Channel extends Closeable, SSHPacketHandler, ErrorNotifiable {
/** Returns the channel ID */ /** Returns the channel ID */
int getID(); int getID();
/** Returns the {@code InputStream} for this channel. */ /** @return the {@code InputStream} for this channel. */
InputStream getInputStream(); InputStream getInputStream();
/** Returns the maximum packet size that we have specified. */ /** @return the maximum packet size that we have specified. */
int getLocalMaxPacketSize(); int getLocalMaxPacketSize();
/** Returns the current local window size. */ /** @return the current local window size. */
int getLocalWinSize(); int getLocalWinSize();
/** Returns an {@code OutputStream} for this channel. */ /** @return an {@code OutputStream} for this channel. */
OutputStream getOutputStream(); OutputStream getOutputStream();
/** Returns the channel ID at the remote end. */ /** @return the channel ID at the remote end. */
int getRecipient(); int getRecipient();
/** Returns the maximum packet size as specified by the remote end. */ /** @return the maximum packet size as specified by the remote end. */
int getRemoteMaxPacketSize(); int getRemoteMaxPacketSize();
/** Returns the current remote window size. */ /** @return the current remote window size. */
int getRemoteWinSize(); int getRemoteWinSize();
/** Returns the channel type identifier. */ /** @return the channel type identifier. */
String getType(); String getType();
/** Returns whether the channel is open. */ /** @return whether the channel is open. */
boolean isOpen(); boolean isOpen();
/** /**
* Sends an EOF message to the server for this channel; indicating that no more data will be sent by us. The {@code * Sends an EOF message to the server for this channel; indicating that no more data will be sent by us. The {@code
* OutputStream} for this channel will be closed and no longer usable. * OutputStream} for this channel will be closed and no longer usable.
*
* @throws TransportException
*/ */
void sendEOF() throws TransportException; void sendEOF()
throws TransportException;
/** /**
* Set whether local window should automatically expand when data is received, irrespective of whether data has been * Set whether local window should automatically expand when data is received, irrespective of whether data has been

View File

@@ -55,7 +55,9 @@ import java.io.InterruptedIOException;
* {@link InputStream} for channels. Can {@link #receive(byte[], int, int) receive} data into its buffer for serving to * {@link InputStream} for channels. Can {@link #receive(byte[], int, int) receive} data into its buffer for serving to
* readers. * readers.
*/ */
public final class ChannelInputStream extends InputStream implements ErrorNotifiable { public final class ChannelInputStream
extends InputStream
implements ErrorNotifiable {
private final Logger log; private final Logger log;
@@ -104,14 +106,16 @@ public final class ChannelInputStream extends InputStream implements ErrorNotifi
} }
@Override @Override
public int read() throws IOException { public int read()
throws IOException {
synchronized (b) { synchronized (b) {
return read(b, 0, 1) == -1 ? -1 : b[0]; return read(b, 0, 1) == -1 ? -1 : b[0];
} }
} }
@Override @Override
public int read(byte[] b, int off, int len) throws IOException { public int read(byte[] b, int off, int len)
throws IOException {
synchronized (buf) { synchronized (buf) {
for (; ;) { for (; ;) {
if (buf.available() > 0) if (buf.available() > 0)
@@ -140,7 +144,8 @@ public final class ChannelInputStream extends InputStream implements ErrorNotifi
return len; return len;
} }
public void receive(byte[] data, int offset, int len) throws ConnectionException, TransportException { public void receive(byte[] data, int offset, int len)
throws ConnectionException, TransportException {
if (eof) if (eof)
throw new ConnectionException("Getting data on EOF'ed stream"); throw new ConnectionException("Getting data on EOF'ed stream");
synchronized (buf) { synchronized (buf) {
@@ -152,12 +157,14 @@ public final class ChannelInputStream extends InputStream implements ErrorNotifi
checkWindow(); checkWindow();
} }
private void checkWindow() throws TransportException { private void checkWindow()
throws TransportException {
synchronized (win) { synchronized (win) {
final int adjustment = win.neededAdjustment(); final int adjustment = win.neededAdjustment();
if (adjustment > 0) { if (adjustment > 0) {
log.info("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST to #{} for {} bytes", chan.getRecipient(), adjustment); 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)); trans.write(new SSHPacket(Message.CHANNEL_WINDOW_ADJUST)
.putInt(chan.getRecipient()).putInt(adjustment));
win.expand(adjustment); win.expand(adjustment);
} }
} }

View File

@@ -49,7 +49,9 @@ import java.io.OutputStream;
* {@link OutputStream} for channels. Buffers data upto the remote window's maximum packet size. Data can also be * {@link OutputStream} for channels. Buffers data upto the remote window's maximum packet size. Data can also be
* flushed via {@link #flush()} and is also flushed on {@link #close()}. * flushed via {@link #flush()} and is also flushed on {@link #close()}.
*/ */
public final class ChannelOutputStream extends OutputStream implements ErrorNotifiable { public final class ChannelOutputStream
extends OutputStream
implements ErrorNotifiable {
private final Channel chan; private final Channel chan;
private Transport trans; private Transport trans;
@@ -77,13 +79,15 @@ public final class ChannelOutputStream extends OutputStream implements ErrorNoti
} }
@Override @Override
public synchronized void write(int w) throws IOException { public synchronized void write(int w)
throws IOException {
b[0] = (byte) w; b[0] = (byte) w;
write(b, 0, 1); write(b, 0, 1);
} }
@Override @Override
public synchronized void write(byte[] data, int off, int len) throws IOException { public synchronized void write(byte[] data, int off, int len)
throws IOException {
checkClose(); checkClose();
while (len > 0) { while (len > 0) {
final int x = Math.min(len, win.getMaxPacketSize() - bufferLength); final int x = Math.min(len, win.getMaxPacketSize() - bufferLength);
@@ -102,7 +106,8 @@ public final class ChannelOutputStream extends OutputStream implements ErrorNoti
this.error = error; this.error = error;
} }
private synchronized void checkClose() throws SSHException { private synchronized void checkClose()
throws SSHException {
if (closed) if (closed)
if (error != null) if (error != null)
throw error; throw error;
@@ -111,7 +116,8 @@ public final class ChannelOutputStream extends OutputStream implements ErrorNoti
} }
@Override @Override
public synchronized void close() throws IOException { public synchronized void close()
throws IOException {
if (!closed) if (!closed)
try { try {
flush(); flush();
@@ -126,7 +132,8 @@ public final class ChannelOutputStream extends OutputStream implements ErrorNoti
} }
@Override @Override
public synchronized void flush() throws IOException { public synchronized void flush()
throws IOException {
checkClose(); checkClose();
if (bufferLength <= 0) // No data to send if (bufferLength <= 0) // No data to send

View File

@@ -12,35 +12,20 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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; package net.schmizz.sshj.connection.channel;
import net.schmizz.sshj.connection.ConnectionException; import net.schmizz.sshj.connection.ConnectionException;
public class OpenFailException extends ConnectionException { public class OpenFailException
extends ConnectionException {
public enum Reason { public enum Reason {
UNKNOWN(0), ADMINISTRATIVELY_PROHIBITED(1), CONNECT_FAILED(2), UNKNOWN_CHANNEL_TYPE(3), RESOURCE_SHORTAGE(4); UNKNOWN(0),
ADMINISTRATIVELY_PROHIBITED(1),
CONNECT_FAILED(2),
UNKNOWN_CHANNEL_TYPE(3),
RESOURCE_SHORTAGE(4);
public static Reason fromInt(int code) { public static Reason fromInt(int code) {
for (Reason rc : Reason.values()) for (Reason rc : Reason.values())

View File

@@ -68,13 +68,15 @@ public abstract class Window {
} }
/** Controls how much data we can send before an adjustment notification from remote end is required. */ /** Controls how much data we can send before an adjustment notification from remote end is required. */
public static final class Remote extends Window { public static final class Remote
extends Window {
public Remote(int chanID, int initialWinSize, int maxPacketSize) { public Remote(int chanID, int initialWinSize, int maxPacketSize) {
super(chanID, "remote win", initialWinSize, maxPacketSize); super(chanID, "remote win", initialWinSize, maxPacketSize);
} }
public void waitAndConsume(int howMuch) throws ConnectionException { public void waitAndConsume(int howMuch)
throws ConnectionException {
synchronized (lock) { synchronized (lock) {
while (size < howMuch) { while (size < howMuch) {
log.debug("Waiting, need window space for {} bytes", howMuch); log.debug("Waiting, need window space for {} bytes", howMuch);
@@ -91,7 +93,8 @@ public abstract class Window {
} }
/** Controls how much data remote end can send before an adjustment notification from us is required. */ /** Controls how much data remote end can send before an adjustment notification from us is required. */
public static final class Local extends Window { public static final class Local
extends Window {
private final int initialSize; private final int initialSize;
private final int threshold; private final int threshold;
@@ -102,7 +105,8 @@ public abstract class Window {
threshold = Math.min(maxPacketSize * 20, initialSize / 4); threshold = Math.min(maxPacketSize * 20, initialSize / 4);
} }
public int neededAdjustment() throws TransportException { public int neededAdjustment()
throws TransportException {
synchronized (lock) { synchronized (lock) {
return (size - threshold <= 0) ? (initialSize - size) : 0; return (size - threshold <= 0) ? (initialSize - size) : 0;
} }

View File

@@ -47,7 +47,9 @@ import net.schmizz.sshj.transport.TransportException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** Base class for direct channels whose open is initated by the client. */ /** Base class for direct channels whose open is initated by the client. */
public abstract class AbstractDirectChannel extends AbstractChannel implements Channel.Direct { public abstract class AbstractDirectChannel
extends AbstractChannel
implements Channel.Direct {
protected AbstractDirectChannel(String name, Connection conn) { protected AbstractDirectChannel(String name, Connection conn) {
super(name, conn); super(name, conn);
@@ -58,7 +60,8 @@ public abstract class AbstractDirectChannel extends AbstractChannel implements C
conn.attach(this); conn.attach(this);
} }
public void open() throws ConnectionException, TransportException { public void open()
throws ConnectionException, TransportException {
trans.write(buildOpenReq()); trans.write(buildOpenReq());
open.await(conn.getTimeout(), TimeUnit.SECONDS); open.await(conn.getTimeout(), TimeUnit.SECONDS);
} }
@@ -82,7 +85,8 @@ public abstract class AbstractDirectChannel extends AbstractChannel implements C
} }
@Override @Override
protected void gotUnknown(Message cmd, SSHPacket buf) throws ConnectionException, TransportException { protected void gotUnknown(Message cmd, SSHPacket buf)
throws ConnectionException, TransportException {
switch (cmd) { switch (cmd) {
case CHANNEL_OPEN_CONFIRMATION: case CHANNEL_OPEN_CONFIRMATION:

View File

@@ -51,7 +51,8 @@ import java.net.SocketAddress;
public class LocalPortForwarder { public class LocalPortForwarder {
private class DirectTCPIPChannel extends AbstractDirectChannel { private class DirectTCPIPChannel
extends AbstractDirectChannel {
private final Socket sock; private final Socket sock;
@@ -60,13 +61,15 @@ public class LocalPortForwarder {
this.sock = sock; this.sock = sock;
} }
private void start() throws IOException { private void start()
throws IOException {
sock.setSendBufferSize(getLocalMaxPacketSize()); sock.setSendBufferSize(getLocalMaxPacketSize());
sock.setReceiveBufferSize(getRemoteMaxPacketSize()); sock.setReceiveBufferSize(getRemoteMaxPacketSize());
final ErrorCallback closer = StreamCopier.closeOnErrorCallback(this, final ErrorCallback closer = StreamCopier.closeOnErrorCallback(this,
new Closeable() { new Closeable() {
public void close() throws IOException { public void close()
throws IOException {
sock.close(); sock.close();
} }
}); });
@@ -102,7 +105,8 @@ public class LocalPortForwarder {
private final String host; private final String host;
private final int port; private final int port;
public LocalPortForwarder(Connection conn, SocketAddress listeningAddr, String host, int port) throws IOException { public LocalPortForwarder(Connection conn, SocketAddress listeningAddr, String host, int port)
throws IOException {
this(ServerSocketFactory.getDefault(), conn, listeningAddr, host, port); this(ServerSocketFactory.getDefault(), conn, listeningAddr, host, port);
} }
@@ -118,7 +122,8 @@ public class LocalPortForwarder {
* *
* @throws IOException if there is an error binding on specified {@code listeningAddr} * @throws IOException if there is an error binding on specified {@code listeningAddr}
*/ */
public LocalPortForwarder(ServerSocketFactory ssf, Connection conn, SocketAddress listeningAddr, String host, int port) throws IOException { public LocalPortForwarder(ServerSocketFactory ssf, Connection conn, SocketAddress listeningAddr, String host, int port)
throws IOException {
this.conn = conn; this.conn = conn;
this.host = host; this.host = host;
this.port = port; this.port = port;
@@ -132,7 +137,8 @@ public class LocalPortForwarder {
} }
/** Start listening for incoming connections and forward to remote host as a channel. */ /** Start listening for incoming connections and forward to remote host as a channel. */
public void listen() throws IOException { public void listen()
throws IOException {
log.info("Listening on {}", ss.getLocalSocketAddress()); log.info("Listening on {}", ss.getLocalSocketAddress());
Socket sock; Socket sock;
while (true) { while (true) {

View File

@@ -56,10 +56,12 @@ import java.util.Map;
* @see Shell * @see Shell
* @see Subsystem * @see Subsystem
*/ */
public interface Session extends Channel { public interface Session
extends Channel {
/** Command API. */ /** Command API. */
interface Command extends Channel { interface Command
extends Channel {
/** /**
* Read from the command's {@code stderr} stream into a string (blocking). * Read from the command's {@code stderr} stream into a string (blocking).
@@ -68,7 +70,8 @@ public interface Session extends Channel {
* *
* @throws IOException if error reading from the stream * @throws IOException if error reading from the stream
*/ */
String getErrorAsString() throws IOException; String getErrorAsString()
throws IOException;
/** Returns the command's {@code stderr} stream. */ /** Returns the command's {@code stderr} stream. */
InputStream getErrorStream(); InputStream getErrorStream();
@@ -105,7 +108,8 @@ public interface Session extends Channel {
* *
* @throws IOException if error reading from the stream * @throws IOException if error reading from the stream
*/ */
String getOutputAsString() throws IOException; String getOutputAsString()
throws IOException;
/** /**
* Send a signal to the remote command. * Send a signal to the remote command.
@@ -114,12 +118,14 @@ public interface Session extends Channel {
* *
* @throws TransportException if error sending the signal * @throws TransportException if error sending the signal
*/ */
void signal(Signal signal) throws TransportException; void signal(Signal signal)
throws TransportException;
} }
/** Shell API. */ /** Shell API. */
interface Shell extends Channel { interface Shell
extends Channel {
/** /**
* Whether the client can do local flow control using {@code control-S} and {@code control-Q}. * Whether the client can do local flow control using {@code control-S} and {@code control-Q}.
@@ -139,7 +145,8 @@ public interface Session extends Channel {
* *
* @throws TransportException * @throws TransportException
*/ */
void changeWindowDimensions(int cols, int rows, int width, int height) throws TransportException; void changeWindowDimensions(int cols, int rows, int width, int height)
throws TransportException;
/** Returns the shell's {@code stderr} stream. */ /** Returns the shell's {@code stderr} stream. */
InputStream getErrorStream(); InputStream getErrorStream();
@@ -151,12 +158,14 @@ public interface Session extends Channel {
* *
* @throws TransportException if error sending the signal * @throws TransportException if error sending the signal
*/ */
void signal(Signal signal) throws TransportException; void signal(Signal signal)
throws TransportException;
} }
/** Subsystem API. */ /** Subsystem API. */
interface Subsystem extends Channel { interface Subsystem
extends Channel {
Integer getExitStatus(); Integer getExitStatus();
} }
@@ -167,7 +176,8 @@ public interface Session extends Channel {
* *
* @throws TransportException * @throws TransportException
*/ */
void allocateDefaultPTY() throws ConnectionException, TransportException; void allocateDefaultPTY()
throws ConnectionException, TransportException;
/** /**
* Allocate a psuedo-terminal for this session. * Allocate a psuedo-terminal for this session.
@@ -197,7 +207,8 @@ public interface Session extends Channel {
* @throws ConnectionException if the request to execute the command failed * @throws ConnectionException if the request to execute the command failed
* @throws TransportException if there is an error sending the request * @throws TransportException if there is an error sending the request
*/ */
Command exec(String command) throws ConnectionException, TransportException; Command exec(String command)
throws ConnectionException, TransportException;
/** /**
* Request X11 forwarding. * Request X11 forwarding.
@@ -209,7 +220,8 @@ public interface Session extends Channel {
* @throws ConnectionException if the request failed * @throws ConnectionException if the request failed
* @throws TransportException if there was an error sending the request * @throws TransportException if there was an error sending the request
*/ */
void reqX11Forwarding(String authProto, String authCookie, int screen) throws ConnectionException, void reqX11Forwarding(String authProto, String authCookie, int screen)
throws ConnectionException,
TransportException; TransportException;
/** /**
@@ -221,7 +233,8 @@ public interface Session extends Channel {
* @throws ConnectionException if the request failed * @throws ConnectionException if the request failed
* @throws TransportException if there was an error sending the request * @throws TransportException if there was an error sending the request
*/ */
void setEnvVar(String name, String value) throws ConnectionException, TransportException; void setEnvVar(String name, String value)
throws ConnectionException, TransportException;
/** /**
* Request a shell. * Request a shell.
@@ -231,7 +244,8 @@ public interface Session extends Channel {
* @throws ConnectionException if the request failed * @throws ConnectionException if the request failed
* @throws TransportException if there was an error sending the request * @throws TransportException if there was an error sending the request
*/ */
Shell startShell() throws ConnectionException, TransportException; Shell startShell()
throws ConnectionException, TransportException;
/** /**
* Request a subsystem. * Request a subsystem.
@@ -243,6 +257,7 @@ public interface Session extends Channel {
* @throws ConnectionException if the request failed * @throws ConnectionException if the request failed
* @throws TransportException if there was an error sending the request * @throws TransportException if there was an error sending the request
*/ */
Subsystem startSubsystem(String name) throws ConnectionException, TransportException; Subsystem startSubsystem(String name)
throws ConnectionException, TransportException;
} }

View File

@@ -52,8 +52,9 @@ import java.util.concurrent.TimeUnit;
/** {@link Session} implementation. */ /** {@link Session} implementation. */
public class public class
SessionChannel extends AbstractDirectChannel implements Session, Session.Command, Session.Shell, SessionChannel
Session.Subsystem { extends AbstractDirectChannel
implements Session, Session.Command, Session.Shell, Session.Subsystem {
private Integer exitStatus; private Integer exitStatus;
@@ -69,7 +70,8 @@ public class
super("session", conn); super("session", conn);
} }
public void allocateDefaultPTY() throws ConnectionException, TransportException { public void allocateDefaultPTY()
throws ConnectionException, TransportException {
// TODO FIXME (maybe?): These modes were originally copied from what SSHD was doing; // TODO FIXME (maybe?): These modes were originally copied from what SSHD was doing;
// and then the echo modes were set to 0 to better serve the PTY example. // and then the echo modes were set to 0 to better serve the PTY example.
// Not sure what default PTY modes should be. // Not sure what default PTY modes should be.
@@ -103,7 +105,8 @@ public class
return canDoFlowControl; return canDoFlowControl;
} }
public void changeWindowDimensions(int cols, int rows, int width, int height) throws TransportException { public void changeWindowDimensions(int cols, int rows, int width, int height)
throws TransportException {
sendChannelRequest( sendChannelRequest(
"pty-req", "pty-req",
false, false,
@@ -115,13 +118,16 @@ public class
); );
} }
public Command exec(String command) throws ConnectionException, TransportException { public Command exec(String command)
throws ConnectionException, TransportException {
log.info("Will request to exec `{}`", command); log.info("Will request to exec `{}`", command);
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command)).await(conn.getTimeout(), TimeUnit.SECONDS); sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command))
.await(conn.getTimeout(), TimeUnit.SECONDS);
return this; return this;
} }
public String getErrorAsString() throws IOException { public String getErrorAsString()
throws IOException {
return StreamCopier.copyStreamToString(err); return StreamCopier.copyStreamToString(err);
} }
@@ -141,12 +147,14 @@ public class
return exitStatus; return exitStatus;
} }
public String getOutputAsString() throws IOException { public String getOutputAsString()
throws IOException {
return StreamCopier.copyStreamToString(getInputStream()); return StreamCopier.copyStreamToString(getInputStream());
} }
@Override @Override
public void handleRequest(String req, SSHPacket buf) throws ConnectionException, TransportException { public void handleRequest(String req, SSHPacket buf)
throws ConnectionException, TransportException {
if ("xon-xoff".equals(req)) if ("xon-xoff".equals(req))
canDoFlowControl = buf.readBoolean(); canDoFlowControl = buf.readBoolean();
else if ("exit-status".equals(req)) else if ("exit-status".equals(req))
@@ -160,7 +168,8 @@ public class
super.handleRequest(req, buf); super.handleRequest(req, buf);
} }
public void reqX11Forwarding(String authProto, String authCookie, int screen) throws ConnectionException, public void reqX11Forwarding(String authProto, String authCookie, int screen)
throws ConnectionException,
TransportException { TransportException {
sendChannelRequest( sendChannelRequest(
"x11-req", "x11-req",
@@ -173,22 +182,28 @@ public class
).await(conn.getTimeout(), TimeUnit.SECONDS); ).await(conn.getTimeout(), TimeUnit.SECONDS);
} }
public void setEnvVar(String name, String value) throws ConnectionException, TransportException { public void setEnvVar(String name, String value)
sendChannelRequest("env", true, new Buffer.PlainBuffer().putString(name).putString(value)).await(conn.getTimeout(), TimeUnit.SECONDS); throws ConnectionException, TransportException {
sendChannelRequest("env", true, new Buffer.PlainBuffer().putString(name).putString(value))
.await(conn.getTimeout(), TimeUnit.SECONDS);
} }
public void signal(Signal sig) throws TransportException { public void signal(Signal sig)
throws TransportException {
sendChannelRequest("signal", false, new Buffer.PlainBuffer().putString(sig.toString())); sendChannelRequest("signal", false, new Buffer.PlainBuffer().putString(sig.toString()));
} }
public Shell startShell() throws ConnectionException, TransportException { public Shell startShell()
throws ConnectionException, TransportException {
sendChannelRequest("shell", true, null).await(conn.getTimeout(), TimeUnit.SECONDS); sendChannelRequest("shell", true, null).await(conn.getTimeout(), TimeUnit.SECONDS);
return this; return this;
} }
public Subsystem startSubsystem(String name) throws ConnectionException, TransportException { public Subsystem startSubsystem(String name)
throws ConnectionException, TransportException {
log.info("Will request `{}` subsystem", name); log.info("Will request `{}` subsystem", name);
sendChannelRequest("subsystem", true, new Buffer.PlainBuffer().putString(name)).await(conn.getTimeout(), TimeUnit.SECONDS); sendChannelRequest("subsystem", true, new Buffer.PlainBuffer().putString(name))
.await(conn.getTimeout(), TimeUnit.SECONDS);
return this; return this;
} }
@@ -209,7 +224,8 @@ public class
} }
@Override @Override
protected void gotExtendedData(int dataTypeCode, SSHPacket buf) throws ConnectionException, TransportException { protected void gotExtendedData(int dataTypeCode, SSHPacket buf)
throws ConnectionException, TransportException {
if (dataTypeCode == 1) if (dataTypeCode == 1)
receiveInto(err, buf); receiveInto(err, buf);
else else

View File

@@ -30,6 +30,7 @@ public interface SessionFactory {
* @throws SSHException * @throws SSHException
* @see {@link Session} * @see {@link Session}
*/ */
Session startSession() throws SSHException; Session startSession()
throws SSHException;
} }

View File

@@ -39,8 +39,21 @@ package net.schmizz.sshj.connection.channel.direct;
/** Various signals that may be sent or received. The signals are from POSIX and simply miss the {@code "SIG_"} prefix. */ /** Various signals that may be sent or received. The signals are from POSIX and simply miss the {@code "SIG_"} prefix. */
public enum Signal { public enum Signal {
ABRT("ABRT"), ALRM("ALRM"), FPE("FPE"), HUP("HUP"), ILL("ILL"), INT("INT"), KILL("KILL"), PIPE("PIPE"), QUIT( ABRT("ABRT"),
"QUIT"), SEGV("SEGV"), TERM("TERM"), USR1("USR1"), USR2("USR2"), UNKNOWN("UNKNOWN"); ALRM("ALRM"),
FPE("FPE"),
HUP("HUP"),
ILL("ILL"),
INT("INT"),
KILL("KILL"),
PIPE("PIPE"),
QUIT(
"QUIT"),
SEGV("SEGV"),
TERM("TERM"),
USR1("USR1"),
USR2("USR2"),
UNKNOWN("UNKNOWN");
/** /**
* Create from the string representation used when the signal is received as part of an SSH packet. * Create from the string representation used when the signal is received as part of an SSH packet.

View File

@@ -43,7 +43,9 @@ import net.schmizz.sshj.connection.channel.OpenFailException.Reason;
import net.schmizz.sshj.transport.TransportException; import net.schmizz.sshj.transport.TransportException;
/** Base class for forwarded channels whose open is initiated by the server. */ /** Base class for forwarded channels whose open is initiated by the server. */
public abstract class AbstractForwardedChannel extends AbstractChannel implements Channel.Forwarded { public abstract class AbstractForwardedChannel
extends AbstractChannel
implements Channel.Forwarded {
protected final String origIP; protected final String origIP;
protected final int origPort; protected final int origPort;
@@ -60,7 +62,8 @@ public abstract class AbstractForwardedChannel extends AbstractChannel implement
init(recipient, remoteWinSize, remoteMaxPacketSize); init(recipient, remoteWinSize, remoteMaxPacketSize);
} }
public void confirm() throws TransportException { public void confirm()
throws TransportException {
log.info("Confirming `{}` channel #{}", getType(), getID()); log.info("Confirming `{}` channel #{}", getType(), getID());
// Must ensure channel is attached before confirming, data could start coming in immediately! // Must ensure channel is attached before confirming, data could start coming in immediately!
conn.attach(this); conn.attach(this);
@@ -71,7 +74,8 @@ public abstract class AbstractForwardedChannel extends AbstractChannel implement
open.set(); open.set();
} }
public void reject(Reason reason, String message) throws TransportException { public void reject(Reason reason, String message)
throws TransportException {
log.info("Rejecting `{}` channel: {}", getType(), message); log.info("Rejecting `{}` channel: {}", getType(), message);
conn.sendOpenFailure(getRecipient(), reason, message); conn.sendOpenFailure(getRecipient(), reason, message);
} }

View File

@@ -12,26 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.forwarded; package net.schmizz.sshj.connection.channel.forwarded;
@@ -47,7 +27,8 @@ import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
/** Base class for {@link net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener}'s. */ /** Base class for {@link net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener}'s. */
public abstract class AbstractForwardedChannelOpener implements ForwardedChannelOpener { public abstract class AbstractForwardedChannelOpener
implements ForwardedChannelOpener {
protected final Logger log = LoggerFactory.getLogger(getClass()); protected final Logger log = LoggerFactory.getLogger(getClass());

View File

@@ -24,13 +24,14 @@ public interface ConnectListener {
/** /**
* Notify this listener of a new forwarded channel. An implementation should firstly {@link * Notify this listener of a new forwarded channel. An implementation should firstly {@link
* net.schmizz.sshj.connection.channel.Channel.Forwarded#confirm() confirm} or {@link * Channel.Forwarded#confirm() confirm} or {@link Channel.Forwarded#reject(net.schmizz.sshj.connection.channel.OpenFailException.Reason,
* net.schmizz.sshj.connection.channel.Channel.Forwarded#reject() reject} that channel. * String)} reject} that channel.
* *
* @param chan the {@link net.schmizz.sshj.connection.channel.Channel.Forwarded forwarded channel} * @param chan the {@link net.schmizz.sshj.connection.channel.Channel.Forwarded forwarded channel}
* *
* @throws java.io.IOException * @throws IOException
*/ */
void gotConnect(Channel.Forwarded chan) throws IOException; void gotConnect(Channel.Forwarded chan)
throws IOException;
} }

View File

@@ -31,6 +31,7 @@ public interface ForwardedChannelOpener {
* @param buf {@link net.schmizz.sshj.common.SSHPacket} containg the request except for the message identifier and * @param buf {@link net.schmizz.sshj.common.SSHPacket} containg the request except for the message identifier and
* channel type field * channel type field
*/ */
void handleOpen(SSHPacket buf) throws ConnectionException, TransportException; void handleOpen(SSHPacket buf)
throws ConnectionException, TransportException;
} }

View File

@@ -12,26 +12,6 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.forwarded; package net.schmizz.sshj.connection.channel.forwarded;
@@ -48,7 +28,8 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** Handles remote port forwarding. */ /** Handles remote port forwarding. */
public class RemotePortForwarder extends AbstractForwardedChannelOpener { public class RemotePortForwarder
extends AbstractForwardedChannelOpener {
/** /**
* Represents a particular forwarding. From RFC 4254, s. 7.1 * Represents a particular forwarding. From RFC 4254, s. 7.1
@@ -139,14 +120,16 @@ public class RemotePortForwarder extends AbstractForwardedChannelOpener {
} }
/** A {@code forwarded-tcpip} channel. */ /** A {@code forwarded-tcpip} channel. */
public static class ForwardedTCPIPChannel extends AbstractForwardedChannel { public static class ForwardedTCPIPChannel
extends AbstractForwardedChannel {
public static final String TYPE = "forwarded-tcpip"; public static final String TYPE = "forwarded-tcpip";
private final Forward fwd; private final Forward fwd;
public ForwardedTCPIPChannel(Connection conn, int recipient, int remoteWinSize, int remoteMaxPacketSize, public ForwardedTCPIPChannel(Connection conn, int recipient, int remoteWinSize, int remoteMaxPacketSize,
Forward fwd, String origIP, int origPort) throws TransportException { Forward fwd, String origIP, int origPort)
throws TransportException {
super(TYPE, conn, recipient, remoteWinSize, remoteMaxPacketSize, origIP, origPort); super(TYPE, conn, recipient, remoteWinSize, remoteMaxPacketSize, origIP, origPort);
this.fwd = fwd; this.fwd = fwd;
} }
@@ -182,7 +165,8 @@ public class RemotePortForwarder extends AbstractForwardedChannelOpener {
* @throws ConnectionException if there is an error requesting the forwarding * @throws ConnectionException if there is an error requesting the forwarding
* @throws TransportException * @throws TransportException
*/ */
public Forward bind(Forward forward, ConnectListener listener) throws ConnectionException, TransportException { public Forward bind(Forward forward, ConnectListener listener)
throws ConnectionException, TransportException {
SSHPacket reply = req(PF_REQ, forward); SSHPacket reply = req(PF_REQ, forward);
if (forward.port == 0) if (forward.port == 0)
forward.port = reply.readInt(); forward.port = reply.readInt();
@@ -199,7 +183,8 @@ public class RemotePortForwarder extends AbstractForwardedChannelOpener {
* @throws ConnectionException if there is an error with the cancellation request * @throws ConnectionException if there is an error with the cancellation request
* @throws TransportException * @throws TransportException
*/ */
public void cancel(Forward forward) throws ConnectionException, TransportException { public void cancel(Forward forward)
throws ConnectionException, TransportException {
try { try {
req(PF_CANCEL, forward); req(PF_CANCEL, forward);
} finally { } finally {
@@ -207,8 +192,10 @@ public class RemotePortForwarder extends AbstractForwardedChannelOpener {
} }
} }
protected SSHPacket req(String reqName, Forward forward) throws ConnectionException, TransportException { protected SSHPacket req(String reqName, Forward forward)
final byte[] specifics = new Buffer.PlainBuffer().putString(forward.address).putInt(forward.port).getCompactData(); throws ConnectionException, TransportException {
final byte[] specifics = new Buffer.PlainBuffer().putString(forward.address).putInt(forward.port)
.getCompactData();
return conn.sendGlobalRequest(reqName, true, specifics) return conn.sendGlobalRequest(reqName, true, specifics)
.get(conn.getTimeout(), TimeUnit.SECONDS); .get(conn.getTimeout(), TimeUnit.SECONDS);
} }
@@ -222,7 +209,8 @@ public class RemotePortForwarder extends AbstractForwardedChannelOpener {
* Internal API. Creates a {@link ForwardedTCPIPChannel} from the {@code CHANNEL_OPEN} request and calls associated * Internal API. Creates a {@link ForwardedTCPIPChannel} from the {@code CHANNEL_OPEN} request and calls associated
* {@code ConnectListener} for that forward in a separate thread. * {@code ConnectListener} for that forward in a separate thread.
*/ */
public void handleOpen(SSHPacket buf) throws ConnectionException, TransportException { public void handleOpen(SSHPacket buf)
throws ConnectionException, TransportException {
final ForwardedTCPIPChannel chan = new ForwardedTCPIPChannel(conn, buf.readInt(), buf.readInt(), buf.readInt(), final ForwardedTCPIPChannel chan = new ForwardedTCPIPChannel(conn, buf.readInt(), buf.readInt(), buf.readInt(),
new Forward(buf.readString(), buf.readInt()), new Forward(buf.readString(), buf.readInt()),
buf.readString(), buf.readInt()); buf.readString(), buf.readInt());

View File

@@ -30,7 +30,8 @@ import java.net.SocketAddress;
* A {@link net.schmizz.sshj.connection.channel.forwarded.ConnectListener} that forwards what is received over the * A {@link net.schmizz.sshj.connection.channel.forwarded.ConnectListener} that forwards what is received over the
* channel to a socket and vice-versa. * channel to a socket and vice-versa.
*/ */
public class SocketForwardingConnectListener implements ConnectListener { public class SocketForwardingConnectListener
implements ConnectListener {
protected final Logger log = LoggerFactory.getLogger(getClass()); protected final Logger log = LoggerFactory.getLogger(getClass());
@@ -42,7 +43,8 @@ public class SocketForwardingConnectListener implements ConnectListener {
} }
/** On connect, confirm the channel and start forwarding. */ /** On connect, confirm the channel and start forwarding. */
public void gotConnect(Channel.Forwarded chan) throws IOException { public void gotConnect(Channel.Forwarded chan)
throws IOException {
log.info("New connection from " + chan.getOriginatorIP() + ":" + chan.getOriginatorPort()); log.info("New connection from " + chan.getOriginatorIP() + ":" + chan.getOriginatorPort());
final Socket sock = new Socket(); final Socket sock = new Socket();
@@ -55,7 +57,8 @@ public class SocketForwardingConnectListener implements ConnectListener {
chan.confirm(); chan.confirm();
final ErrorCallback closer = StreamCopier.closeOnErrorCallback(chan, new Closeable() { final ErrorCallback closer = StreamCopier.closeOnErrorCallback(chan, new Closeable() {
public void close() throws IOException { public void close()
throws IOException {
sock.close(); sock.close();
} }
}); });

View File

@@ -24,10 +24,12 @@ import net.schmizz.sshj.transport.TransportException;
* Handles forwarded {@code x11} channels. The actual request to forward X11 should be made from the specific {@link * Handles forwarded {@code x11} channels. The actual request to forward X11 should be made from the specific {@link
* net.schmizz.sshj.connection.channel.direct.Session}. * net.schmizz.sshj.connection.channel.direct.Session}.
*/ */
public class X11Forwarder extends AbstractForwardedChannelOpener { public class X11Forwarder
extends AbstractForwardedChannelOpener {
/** An {@code x11} forwarded channel. */ /** An {@code x11} forwarded channel. */
public static class X11Channel extends AbstractForwardedChannel { public static class X11Channel
extends AbstractForwardedChannel {
public static final String TYPE = "x11"; public static final String TYPE = "x11";
@@ -52,7 +54,8 @@ public class X11Forwarder extends AbstractForwardedChannelOpener {
} }
/** Internal API */ /** Internal API */
public void handleOpen(SSHPacket buf) throws ConnectionException, TransportException { public void handleOpen(SSHPacket buf)
throws ConnectionException, TransportException {
callListener(listener, new X11Channel(conn, buf.readInt(), buf.readInt(), buf.readInt(), buf.readString(), buf callListener(listener, new X11Channel(conn, buf.readInt(), buf.readInt(), buf.readInt(), buf.readString(), buf
.readInt())); .readInt()));
} }