Compare commits

..

146 Commits

Author SHA1 Message Date
Shikhar Bhushan
42dddc7f7e [maven-release-plugin] prepare release v0.6.1 2011-10-02 22:17:36 +01:00
Shikhar Bhushan
f1b3dbb102 Restore mutual exclusion of sendChannelRequest() and gotResponse() in AbstractChannel (but rather than make methods synchronized do it on the queue, which itself doesn't need to be thread-safe). Regression due to 1a2351c5ee. Fixes #35. 2011-10-02 09:47:49 +01:00
Shikhar Bhushan
f83bf2cd3f [maven-release-plugin] prepare for next development iteration 2011-09-26 12:41:23 +01:00
Shikhar Bhushan
be11cbb848 [maven-release-plugin] prepare release v0.6.0 2011-09-26 12:41:17 +01:00
Shikhar Bhushan
43b0599e1f let's make next release sshj 0.6.0 2011-09-26 12:37:21 +01:00
Shikhar Bhushan
b218186cae Merge pull request #34 from hierynomus/scp-filter-copy
Added upload filter capability to SCPUploadClient
2011-09-26 04:11:24 -07:00
hierynomus
184236c3d5 Added upload filter capability to SCPUploadClient 2011-09-26 11:49:07 +02:00
Shikhar Bhushan
cb1d773659 Merge branch 'master' of github.com:shikhar/sshj 2011-09-14 23:06:05 +01:00
Shikhar Bhushan
378665cb46 update contrib 2011-09-14 23:37:31 +02:00
Shikhar Bhushan
a5272dc413 Merge branch 'master' of github.com:shikhar/sshj 2011-09-14 22:11:29 +01:00
Shikhar Bhushan
60552fd001 Merge pull request #31 from hierynomus/filemode-typemask
Fixed bug in SFTP FileMode for AIX/Unix directory mask
2011-09-14 11:43:59 -07:00
hierynomus
ef082c668a Fixed bug in SFTP FileMode for AIX/Unix directory mask 2011-09-14 11:02:35 +02:00
Shikhar Bhushan
e66386eb1c Local window exhaustion -> ConnectionException 2011-09-07 21:45:44 +01:00
Shikhar Bhushan
0937ec9800 minor 2011-09-07 21:24:49 +01:00
Shikhar Bhushan
4b2f42804e Added version in pom.xml for maven-bundle-plugin 2011-08-29 16:49:49 +01:00
Shikhar Bhushan
01765d24d2 AbstractChannel - no reason for channel request response events to share the same underlying lock object. #27 2011-08-29 16:45:12 +01:00
Shikhar Bhushan
1a2351c5ee AbstractChannel - make chanReqResponseEvents a ConcurrentLinkedQueue, so don't have to have sendChannelRequest() & gotResponse() as synchronized methods. #27 2011-08-29 16:41:27 +01:00
Shikhar Bhushan
1cec011401 chown / chmod / chgrp in StatefulSFTPClient -- these all delegate to setattr which is overriden in StatefulSFTPClient to do cwdification. #28 2011-08-29 16:21:10 +01:00
Shikhar Bhushan
52338c13cb Merge pull request #28 from neilprosser/master
StatefulSFTPClient doesn't cwdify all commands
2011-08-27 04:23:07 -07:00
Neil Prosser
09cf21f61a Some stateful methods that needed cwdifying 2011-08-27 00:30:20 +01:00
Shikhar Bhushan
04c2e7b6b8 Client version string update 2011-07-27 19:52:27 +01:00
Shikhar Bhushan
822f196dd8 update contrib 2011-07-27 19:38:56 +01:00
Shikhar Bhushan
a88a574b10 [maven-release-plugin] prepare for next development iteration 2011-07-27 19:33:32 +01:00
Shikhar Bhushan
5cd6986355 [maven-release-plugin] prepare release v0.5.0 2011-07-27 19:33:26 +01:00
Shikhar Bhushan
b5d206bbcb Merge pull request #22 from iocanel/master
OSGi bundle for sshj
2011-07-25 12:22:43 -07:00
Ioannis Canellos
4eae26c551 OSGi-fied sshj 2011-07-25 06:29:20 -07:00
Shikhar Bhushan
b950f88f52 SSHClient implements Closeable 2011-07-24 20:50:01 +01:00
Shikhar Bhushan
3267860db4 mass auto-format 2011-07-24 20:48:00 +01:00
Shikhar Bhushan
d6eb5a040e Non-final ChannelOutputStream field may be null if we haven't received channel open confirmation or channel open failed. So do null-check. 2011-07-16 10:19:46 +01:00
Shikhar Bhushan
21da5b9f65 update contributor list 2011-07-03 14:50:43 -07:00
Shikhar Bhushan
6b66a952d4 Session#changeWindowDimensions was broken, wrong channel request.
Fix for #21
2011-07-03 10:59:40 +01:00
Shikhar Bhushan
aa4faf3f25 Merge pull request #20 from cloudera/forUpstream
Don't leak PrivateKeyStringResource private keys via toString()
2011-06-23 13:46:37 -07:00
Adar Dembo
4be02450dd Don't leak PrivateKeyStringResource private keys via toString() 2011-06-22 17:52:46 -07:00
Shikhar Bhushan
0cec27c28e Edited README.rst via GitHub 2011-06-20 11:44:56 -07:00
Shikhar Bhushan
4384367a1b point to issue tracker 2011-06-20 11:43:21 -07:00
Shikhar Bhushan
4549648a76 [maven-release-plugin] prepare for next development iteration 2011-06-19 18:32:36 +01:00
Shikhar Bhushan
20e2161022 [maven-release-plugin] prepare release v0.4.1 2011-06-19 18:32:30 +01:00
Shikhar Bhushan
fb0f3afa17 fix javadoc warnings 2011-06-19 18:30:52 +01:00
Shikhar Bhushan
114c2bb424 specify missing maven plugin versions & upgrade others 2011-06-19 18:25:04 +01:00
Shikhar Bhushan
079bde5dbf update dependencies in pom 2011-06-19 18:17:06 +01:00
Shikhar Bhushan
eaee42b017 Fix for GH-18
Need to explicitly tell the BigInteger constructor that K (generated secret) is a positive number.
2011-06-19 17:53:56 +01:00
Shikhar Bhushan
8b61d96808 changed some things around, lesser conversions / copying. still not found the bug. grr. 2011-06-19 17:52:00 +01:00
Shikhar Bhushan
73fcc81e83 Unused methods 2011-06-18 20:14:44 +01:00
Shikhar Bhushan
0f7926d4fa . 2011-06-18 14:45:28 +01:00
Shikhar Bhushan
ca6f15650a Useless indirection 2011-06-18 14:44:58 +01:00
Shikhar Bhushan
eb78dc499d . 2011-06-18 13:53:18 +01:00
Shikhar Bhushan
a852f33a15 way to reproduce GH-18 2011-06-18 13:52:03 +01:00
Shikhar Bhushan
ccabc1a20c vestige of eclipse formatting 2011-06-18 13:48:25 +01:00
Shikhar Bhushan
cb2986d32e Update client version string 2011-06-18 13:48:10 +01:00
Shikhar Bhushan
dc70f08e45 Update contributor list 2011-06-08 20:06:23 +01:00
Shikhar Bhushan
bf68ec18b2 Merge pull request #16 from cloudera/forUpstream
Adding support for public key authentication from strings
2011-06-08 11:45:32 -07:00
Philip Langdale
7e78260ca9 Add unit test for String originated key files. 2011-06-07 15:55:44 -07:00
Philip Langdale
27c60cee60 Add support for public key authentication with keys as strings.
Currently, only keys as file locations are supported. This change
adds support for keys as strings.

Significant changes are:

1) Introduction of a new Resource type for keys as strings.

2) Initialization of a key provider with two strings (private and public keys)
   Leaving the public key null is equivalent to not having a .pub file.

3) Obtaining the reader for the resource is refactored into the resource itself
   to avoid requiring knowledge of the type outside the resource.

The loadKeys and authPublickey convenience methods are not duplicated for
the string based loading as we currently don't need them but they could be
if desired (although method signature collisions will be a problem).
2011-06-07 15:55:39 -07:00
Shikhar Bhushan
551b8b4fcf Add forked-path to maven-release-plugin config to allow stdin password input 2011-06-06 20:55:12 +01:00
Shikhar Bhushan
fd591e70be [maven-release-plugin] prepare for next development iteration 2011-06-06 20:44:39 +01:00
Shikhar Bhushan
d177b239c6 [maven-release-plugin] prepare release v0.4.0 2011-06-06 20:44:33 +01:00
Shikhar Bhushan
adf44e2dc0 deleted google group 2011-06-04 13:52:24 -07:00
Shikhar Bhushan
7810b5f653 better example 2011-06-04 20:47:57 +01:00
Shikhar Bhushan
3695e2a184 Buffer underflows as checked exceptions. Should not be a RuntimeException in case we get an invalid SSH packet. 2011-05-30 20:34:13 +01:00
Shikhar Bhushan
17d8e91f05 Renamed some Buffer methods to better reflect the SSH datatype 2011-05-22 09:51:28 +01:00
Shikhar Bhushan
3c3715eccf SFTPEngine carries the instance of PathHelper 2011-05-17 00:19:16 +01:00
Shikhar Bhushan
2ff9f2ae50 Allow specifying alternate path separator (for Windows SFTP implementations) 2011-05-15 11:56:40 +01:00
Shikhar Bhushan
4f7b29da0d UserAuthImpl made plenty cleaner... 2011-05-11 00:08:28 +01:00
Shikhar Bhushan
2d49cb4d77 Resource should implement equals() and hashCode() 2011-05-11 00:08:16 +01:00
Shikhar Bhushan
d752bc36ff Whoops, package-private constructor by mistake for AuthNone 2011-05-11 00:07:58 +01:00
Shikhar Bhushan
99e24b7323 Nicer logging 2011-05-11 00:07:14 +01:00
Shikhar Bhushan
40b401406c It's not a Future, it's a Promise. Rename inspired by https://gist.github.com/959802.
Also Event now delegates to Promise instead of inheriting from it.
2011-05-10 21:51:53 +01:00
Shikhar Bhushan
803b154505 Password prompt in keyboard-interactive auth may have a trailing space 2011-05-03 21:55:34 +01:00
Shikhar Bhushan
ff5935af2a Edited NOTICE via GitHub 2011-05-01 11:24:50 -07:00
Shikhar Bhushan
430ebe27ea Future gets tryGet(), Event gets tryAwait(). ErrorListener disappears from StreamCopier. Socket/channel cleanups for local & remote port forwarding done more consistently with a separate monitoring thread. 2011-04-30 22:35:55 +01:00
Shikhar Bhushan
a0109dd8fa Don't do the caller's job 2011-04-29 11:28:20 +01:00
Shikhar Bhushan
85abcb7aad misc cleanups 2011-04-27 00:13:11 +01:00
Shikhar Bhushan
4de741359e Deprecated Command#getOutputAsString() and Command#getErrorAsString() - same thing can now be done without any decoding ambiguity via IOUtils.pipeStream(InputStream) -> ByteArrayOutputStream 2011-04-24 19:18:09 +01:00
Shikhar Bhushan
ab705d7f2a Consolidated stream copying logic 2011-04-24 19:07:08 +01:00
Shikhar Bhushan
f89c0cc2f0 Minor cleanups 2011-04-10 22:09:02 +01:00
Shikhar Bhushan
d8cc271cd3 SCP path handling: escape path argument to scp command, handle spaces in messages from remote SCP command 2011-04-10 16:16:34 +01:00
Shikhar Bhushan
d1043ea288 Buggy cache 2011-04-10 14:23:19 +01:00
Shikhar Bhushan
ce930c969b Lighter, more coherent interfaces by refactoring LocalFile into LocalSourceFile & LocalDestFile 2011-04-06 23:37:55 +01:00
Shikhar Bhushan
a2c82de260 Carrying on with the file transfer interface refactoring
* Got rid of ModeGetter/ModeSetter, moved that to LocalFile
* Instead of InMemoryFile now InMemorySourceFile (wraps istream) and InMemoryDestFile (wraps ostream)
* Uploading with a LocalFile instance rather than String path
2011-04-05 21:51:03 +01:00
Shikhar Bhushan
2e70b56ba3 License header update 2011-04-05 11:12:25 +01:00
Cyril Ledru
9761f44cd4 LocalFile abstraction in SFTPFileTransfer
Upload method using LocalFile in FileTransfer.
Use only LocalFile in SFTPFileTransfer Upload.
2011-04-03 17:31:04 +08:00
Cyril Ledru
137dc5ed42 Encapsulate SCPEngine
No more SCPEngine variable exposed to the outside.
2011-04-03 17:31:03 +08:00
Cyril Ledru
286a22270b Decoupling between SCP*Clients and SCPEngine
Encapsulate listener inside SCPEngine.
Remove string path in UploadClient.
2011-04-03 17:31:03 +08:00
Cyril Ledru
aa9f4e192f Remove inheritance coupling between SCP*Clients
Use delegation to SCPEngine instead of inheritance.
Remove methods using String path for source file.
2011-04-03 17:31:03 +08:00
Cyril Ledru
41ac277023 Can scp a file without it being on the file system
Introduced an abstraction of a file and two concrete implementation of it:
one "in memory" file and a file on the file system.
2011-04-03 17:31:02 +08:00
Shikhar Bhushan
c56f9997f4 Mostly adapters 2011-03-28 23:14:23 -07:00
Shikhar Bhushan
b92dece6ec . 2011-03-20 09:15:21 -07:00
Shikhar Bhushan
2880fe2bc0 Some more cleanups for handling disconnects 2011-03-14 20:36:49 +00:00
Shikhar Bhushan
ce5fad9809 Add DisconnectListener, refactor tests 2011-03-13 22:51:35 +00:00
Shikhar Bhushan
38883bf15d [maven-release-plugin] prepare for next development iteration 2011-03-02 20:44:31 +00:00
Shikhar Bhushan
20c5ab8dfc [maven-release-plugin] prepare release v0.3.1 2011-03-02 20:44:25 +00:00
Incendium
d9c438ed16 Fixed issue with StatefulSFTPClient.put not transferring files. 2011-03-01 14:53:12 -08:00
Shikhar Bhushan
653e8ad4f2 In SCP, warning means error... 2011-02-27 20:48:26 +00:00
Shikhar Bhushan
c46dc913e8 A Config suitable for running with Android 2.3+ 2011-02-12 23:25:32 +00:00
Shikhar Bhushan
069ebbd47d Try and be helpful on SessionChannel reuse with a more explicit error condition 2011-02-12 20:25:26 +00:00
Shikhar Bhushan
da2cec8fa2 Add a timed join() method to Channel, update Exec example 2011-02-12 20:23:58 +00:00
Shikhar Bhushan
75caa8bcf3 Need to fix my intellij setup on linux... 2010-12-30 22:48:09 +00:00
Shikhar Bhushan
f664b7b24f Merge branch 'master' of github.com:shikhar/sshj 2010-12-30 22:43:34 +00:00
shikhar
70f3aeee68 SessionChannel should override notifyError() in order to notify the stderr stream 2010-12-30 22:43:00 +00:00
shikhar
882d40a1b6 SessionChannel should override notifyError() in order to notify the stderr stream 2010-12-30 22:38:02 +00:00
Shikhar Bhushan
9649b2f72e lets try this flattr thing 2010-08-22 00:09:04 +01:00
Shikhar Bhushan
79a8d0b3ad [maven-release-plugin] prepare for next development iteration 2010-08-15 19:39:01 +01:00
Shikhar Bhushan
2e7fcfd308 [maven-release-plugin] prepare release v0.3.0 2010-08-15 19:38:43 +01:00
Shikhar Bhushan
946422112d can only help 2010-08-15 19:01:57 +01:00
Shikhar Bhushan
b11f0be894 update readme 2010-08-14 00:34:38 +01:00
Shikhar Bhushan
ba6e5292c8 update readme: 2010-08-14 00:32:35 +01:00
Shikhar Bhushan
c8de9ed915 add Channel.join() 2010-08-13 23:01:35 +01:00
Shikhar Bhushan
7ccd078e52 deps update 2010-08-12 23:41:43 +01:00
Shikhar Bhushan
0aa8d5e141 . 2010-08-12 17:34:49 +01:00
Shikhar Bhushan
2e32bb9aca simplify 2010-08-12 17:31:42 +01:00
Shikhar Bhushan
2f4fa62b14 simplify 2010-08-12 17:22:35 +01:00
Shikhar Bhushan
8a4367cc7a minor doc update 2010-08-12 17:19:28 +01:00
Shikhar Bhushan
168272ad3b simplify DisconnectReason enum 2010-08-10 00:30:59 +01:00
Shikhar Bhushan
17eb5cff0f consolidate bitmask logic and fix subtle bug... 2010-08-08 17:55:30 +01:00
Shikhar Bhushan
ebd5036d64 enum reverse cache objects 2010-08-08 17:45:48 +01:00
Shikhar Bhushan
7797d774ac fixing up hostbased auth 2010-08-07 22:35:38 +01:00
Shikhar Bhushan
888a8f60d7 ws 2010-08-07 22:34:09 +01:00
Shikhar Bhushan
974e88efb4 no need to actually have API client specify sftp protocol version, so long as all versions < MAX_SUPPORTED_VERSIONS are supported. which is true for now! 2010-08-06 22:29:43 +01:00
Shikhar Bhushan
9a4a24737f incorrect sftp version compatibility check 2010-08-06 22:15:42 +01:00
Shikhar Bhushan
a1d17982ae public -> protected 2010-08-06 22:10:35 +01:00
Shikhar Bhushan
3beee8350d support sftp versions < 3 2010-08-06 00:02:21 +01:00
Shikhar Bhushan
3cd446b462 dumb 2010-08-06 00:01:51 +01:00
Shikhar Bhushan
486dbf2b05 unused var 2010-08-01 18:34:04 +01:00
Shikhar Bhushan
3cb235bbfd cleanup in transport.Heartbeater 2010-08-01 18:32:06 +01:00
Shikhar Bhushan
2882129211 reduce reliance on implicit channel close; add close() method to SFTP classes; update examples 2010-07-28 23:53:26 +01:00
Shikhar Bhushan
fb97ccb67c in ConnectionImpl#notifyDisconnect() -- why try to close channels politely, they should just be discarded 2010-07-20 23:48:05 +01:00
Shikhar Bhushan
8b21eff1d2 userauth failure log as debug and not error -- if it leads to error it will be raised as an exception anyway 2010-07-20 23:43:48 +01:00
Shikhar Bhushan
7874e7dbfd always use class name for making Loggers 2010-07-20 23:39:51 +01:00
Shikhar Bhushan
efc7702195 one log message is enough 2010-06-29 22:23:24 +01:00
Shikhar Bhushan
34a7b8e065 fixing the fix, just wow... 2010-06-25 22:09:51 +01:00
Shikhar Bhushan
50c42b97a3 sp 2010-06-18 23:10:05 +01:00
Shikhar Bhushan
826660ab3f narrow down error condition: more data is ok, underflow is not 2010-06-17 18:55:53 +01:00
Shikhar Bhushan
a3b6fde44a useless params, useless check, overkill... 2010-06-16 23:52:24 +01:00
Shikhar Bhushan
69555e9c74 update client version ident 2010-06-16 23:18:06 +01:00
Shikhar Bhushan
241f61bdd1 shdve been 0.3.0-SNAPSHOT 2010-06-13 14:35:09 +01:00
Shikhar Bhushan
0051dd420c [maven-release-plugin] prepare for next development iteration 2010-06-13 14:28:34 +01:00
Shikhar Bhushan
f2abc4b397 [maven-release-plugin] prepare release v0.2.3 2010-06-13 14:28:27 +01:00
Shikhar Bhushan
fe0d42fc97 [maven-release-plugin] prepare for next development iteration 2010-06-13 13:58:59 +01:00
Shikhar Bhushan
19e4670c24 [maven-release-plugin] prepare release v0.2.2 2010-06-13 13:58:51 +01:00
Shikhar Bhushan
fbd6e00720 [maven-release-plugin] prepare for next development iteration 2010-06-13 00:11:53 +01:00
Shikhar Bhushan
f69cdb1505 [maven-release-plugin] prepare release v0.2.1 2010-06-13 00:11:42 +01:00
Shikhar Bhushan
135b1c819b dep update 2010-06-13 00:09:11 +01:00
Shikhar Bhushan
9c51b862cd can't throw exception, shit fails on windows 2010-06-12 23:10:47 +01:00
Shikhar Bhushan
a6353cbb2d 2010-05-31 16:24:17 -07:00
Shikhar Bhushan
f11055a726 java 1.6 is a dep 2010-06-01 00:20:38 +01:00
Shikhar Bhushan
da98153ab6 [maven-release-plugin] prepare for next development iteration 2010-05-31 00:02:39 +01:00
198 changed files with 3526 additions and 2362 deletions

8
CONTRIBUTORS Normal file
View File

@@ -0,0 +1,8 @@
Shikhar Bhushan <shikhar@schmizz.net>
Cyril Ledru <cledru@keynectis.net>
Incendium <incendium@gmail.com>
Philip Langdale <philipl@cloudera.com>
Adar Dembo <adar@cloudera.com>
Ioannis Canellos <iocanel@gmail.com>
Neil Prosser <neil.prosser@gmail.com>
hierynomus <jeroen@hierynomus.com>

2
NOTICE
View File

@@ -1,5 +1,5 @@
sshj - SSHv2 library for Java
Copyright 2010 Shikhar Bhushan
Copyright 2010-2011 sshj contributors
This product includes code derived from software developed at
The Apache Software Foundation (http://www.apache.org/):

View File

@@ -6,12 +6,12 @@ To get started, have a look at one of the examples. Hopefully you will find the
Features of the library include:
* reading known_hosts files for host key verification
* password and publickey authentication
* publickey, password and keyboard-interactive authentication
* command, subsystem and shell channels
* local and remote port forwarding
* scp + complete sftp version 3 implementation
* scp + complete sftp version 0-3 implementation
Implementations of the following algorithms are included:
Implementations / adapters for the following algorithms are included:
ciphers
``aes{128,192,256}-{cbc,ctr}``, ``blowfish-cbc``, ``3des-cbc``
@@ -31,20 +31,18 @@ compression
private key files
``pkcs8`` encoded (what openssh uses)
If you need something that is not implemented, it shouldn't be too hard to add (do contribute it!)
If you need something that is not included, it shouldn't be too hard to add (do contribute it!)
Dependencies
-------------
slf4j_ is required. bouncycastle_ is highly recommended and required for using some of the crypto algorithms.
jzlib_ is required for using zlib compression.
Java 6+. slf4j_ is required. bouncycastle_ is highly recommended and required for using some of the crypto algorithms. jzlib_ is required for using zlib compression.
Bugs, questions
----------------
Help and discussion
--------------------
There is a `google group`_.
`Issue tracker <https://github.com/shikhar/sshj/issues>`_
Contributing
@@ -53,12 +51,8 @@ Contributing
Fork away!
.. _buildr: http://buildr.apache.org/installing.html
.. _slf4j: http://www.slf4j.org/download.html
.. _bouncycastle: http://www.bouncycastle.org/java.html
.. _jzlib: http://www.jcraft.com/jzlib/
.. _`google group`: http://groups.google.com/group/sshj
.. _jzlib: http://www.jcraft.com/jzlib/

61
pom.xml
View File

@@ -5,8 +5,8 @@
<groupId>net.schmizz</groupId>
<artifactId>sshj</artifactId>
<packaging>jar</packaging>
<version>0.2.0</version>
<packaging>bundle</packaging>
<version>0.6.1</version>
<name>sshj</name>
<description>SSHv2 library for Java</description>
@@ -37,18 +37,18 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.11</version>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15</artifactId>
<version>1.45</version>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -60,21 +60,27 @@
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>0.4.0</version>
<version>0.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>0.9.20</version>
<version>0.9.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.20</version>
<version>0.9.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0-rc1</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -96,6 +102,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<excludes>
<exclude>examples/*.java</exclude>
@@ -107,11 +114,14 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.0</version>
<version>2.1</version>
<configuration>
<mavenExecutorId>forked-path</mavenExecutorId>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-4</version>
<version>2.2.1</version>
<configuration>
<descriptors>
<descriptor>src/assemble/examples.xml</descriptor>
@@ -130,6 +140,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -142,6 +153,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.8</version>
<configuration>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
@@ -154,6 +166,25 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.5</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>
!net.schmizz.*,
javax.crypto*,
com.jcraft.jzlib*;version="[1.0,2)",
org.slf4j*;version="[1.6,2)",
org.bouncycastle*;version="[1.4,2)",
*
</Import-Package>
<Export-Package>net.schmizz.*</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
@@ -176,7 +207,7 @@
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15</artifactId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.45</version>
</dependency>
<dependency>
@@ -187,12 +218,12 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>0.9.20</version>
<version>0.9.24</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.20</version>
<version>0.9.24</version>
</dependency>
</dependencies>
</profile>
@@ -209,7 +240,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.0</version>
<version>1.3</version>
<configuration>
<passphrase>${gpg.passphrase}</passphrase>
</configuration>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,28 +16,33 @@
package examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/** This examples demonstrates how a remote command can be executed. */
public class Exec {
public static void main(String... args)
throws IOException {
SSHClient ssh = new SSHClient();
final SSHClient ssh = new SSHClient();
ssh.loadKnownHosts();
ssh.connect("localhost");
try {
ssh.authPublickey(System.getProperty("user.name"));
Command cmd = ssh.startSession().exec("ping -c 1 google.com");
System.out.print(cmd.getOutputAsString());
System.out.println("\n** exit status: " + cmd.getExitStatus());
final Session session = ssh.startSession();
try {
final Command cmd = session.exec("ping -c 1 google.com");
System.out.println(IOUtils.readFully(cmd.getInputStream()).toString());
cmd.join(5, TimeUnit.SECONDS);
System.out.println("\n** exit status: " + cmd.getExitStatus());
} finally {
session.close();
}
} finally {
ssh.disconnect();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ public class LocalPF {
* google.com:80
*/
ssh.newLocalPortForwarder(new InetSocketAddress("localhost", 8080), "google.com", 80)
.listen();
.listen();
} finally {
ssh.disconnect();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,11 +46,9 @@ public class RemotePF {
// where the server should listen
new Forward(8080),
// what we do with incoming connections that are forwarded to us
new SocketForwardingConnectListener(new InetSocketAddress("google.com", 80)
));
new SocketForwardingConnectListener(new InetSocketAddress("google.com", 80)));
client.getTransport()
.setHeartbeatInterval(30);
client.getTransport().setHeartbeatInterval(30);
// Something to hang on to so that the forwarding stays
client.getTransport().join();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,35 +37,37 @@ class RudimentaryPTY {
ssh.addHostKeyVerifier(new ConsoleKnownHostsVerifier(khFile, System.console()));
ssh.connect("localhost");
Shell shell = null;
try {
ssh.authPublickey(System.getProperty("user.name"));
final Session session = ssh.startSession();
session.allocateDefaultPTY();
try {
shell = session.startShell();
session.allocateDefaultPTY();
new StreamCopier("stdout", shell.getInputStream(), System.out)
.bufSize(shell.getLocalMaxPacketSize())
.start();
final Shell shell = session.startShell();
new StreamCopier("stderr", shell.getErrorStream(), System.err)
.bufSize(shell.getLocalMaxPacketSize())
.start();
new StreamCopier(shell.getInputStream(), System.out)
.bufSize(shell.getLocalMaxPacketSize())
.spawn("stdout");
// Now make System.in act as stdin. To exit, hit Ctrl+D (since that results in an EOF on System.in)
// This is kinda messy because java only allows console input after you hit return
// But this is just an example... a GUI app could implement a proper PTY
StreamCopier.copy(System.in, shell.getOutputStream(), shell.getRemoteMaxPacketSize(), true);
new StreamCopier(shell.getErrorStream(), System.err)
.bufSize(shell.getLocalMaxPacketSize())
.spawn("stderr");
// Now make System.in act as stdin. To exit, hit Ctrl+D (since that results in an EOF on System.in)
// This is kinda messy because java only allows console input after you hit return
// But this is just an example... a GUI app could implement a proper PTY
new StreamCopier(System.in, shell.getOutputStream())
.bufSize(shell.getRemoteMaxPacketSize())
.copy();
} finally {
session.close();
}
} finally {
if (shell != null)
shell.close();
ssh.disconnect();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.xfer.FileSystemFile;
import java.io.IOException;
@@ -30,9 +31,7 @@ public class SCPDownload {
ssh.connect("localhost");
try {
ssh.authPublickey(System.getProperty("user.name"));
final String src = "test_file";
final String target = "/tmp/";
ssh.newSCPFileTransfer().download(src, target);
ssh.newSCPFileTransfer().download("test_file", new FileSystemFile("/tmp/"));
} finally {
ssh.disconnect();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.xfer.FileSystemFile;
import java.io.File;
import java.io.IOException;
@@ -36,8 +37,7 @@ public class SCPUpload {
ssh.useCompression();
final String src = System.getProperty("user.home") + File.separator + "test_file";
final String target = "/tmp/";
ssh.newSCPFileTransfer().upload(src, target);
ssh.newSCPFileTransfer().upload(new FileSystemFile(src), "/tmp/");
} finally {
ssh.disconnect();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.xfer.FileSystemFile;
import java.io.IOException;
@@ -24,14 +26,17 @@ public class SFTPDownload {
public static void main(String[] args)
throws IOException {
SSHClient ssh = new SSHClient();
final SSHClient ssh = new SSHClient();
ssh.loadKnownHosts();
ssh.connect("localhost");
try {
ssh.authPublickey(System.getProperty("user.name"));
final String src = "test_file";
final String target = "/tmp/";
ssh.newSFTPClient().get(src, target);
final SFTPClient sftp = ssh.newSFTPClient();
try {
sftp.get("test_file", new FileSystemFile("/tmp"));
} finally {
sftp.close();
}
} finally {
ssh.disconnect();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.xfer.FileSystemFile;
import java.io.File;
import java.io.IOException;
@@ -25,14 +27,18 @@ public class SFTPUpload {
public static void main(String[] args)
throws IOException {
SSHClient ssh = new SSHClient();
final SSHClient ssh = new SSHClient();
ssh.loadKnownHosts();
ssh.connect("localhost");
try {
ssh.authPublickey(System.getProperty("user.name"));
final String src = System.getProperty("user.home") + File.separator + "test_file";
final String target = "/tmp/";
ssh.newSFTPClient().put(src, target);
final SFTPClient sftp = ssh.newSFTPClient();
try {
sftp.put(new FileSystemFile(src), "/tmp");
} finally {
sftp.close();
}
} finally {
ssh.disconnect();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@ public class X11 {
public static void main(String... args)
throws IOException, InterruptedException {
SSHClient ssh = new SSHClient();
final SSHClient ssh = new SSHClient();
// Compression makes X11 more feasible over slower connections
// ssh.useCompression();
@@ -55,10 +55,10 @@ public class X11 {
*/
sess.reqX11Forwarding("MIT-MAGIC-COOKIE-1", "b0956167c9ad8f34c8a2788878307dc9", 0);
Command cmd = sess.exec("/usr/X11/bin/xcalc");
final Command cmd = sess.exec("/usr/X11/bin/xcalc");
new StreamCopier("stdout", cmd.getInputStream(), System.out).start();
new StreamCopier("stderr", cmd.getErrorStream(), System.err).start();
new StreamCopier(cmd.getInputStream(), System.out).spawn("stdout");
new StreamCopier(cmd.getErrorStream(), System.err).spawn("stderr");
// Wait for session & X11 channel to get closed
ssh.getConnection().join();

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2010, 2011 sshj contributors
*
* Licensed 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.concurrent;
import java.util.Collection;
public class ErrorDeliveryUtil {
public static void alertPromises(Throwable x, Promise... promises) {
for (Promise p : promises)
p.deliverError(x);
}
public static void alertPromises(Throwable x, Collection<? extends Promise> promises) {
for (Promise p : promises)
p.deliverError(x);
}
public static void alertEvents(Throwable x, Event... events) {
for (Event e : events)
e.deliverError(x);
}
public static void alertEvents(Throwable x, Collection<? extends Event> events) {
for (Event e : events)
e.deliverError(x);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,21 +18,22 @@ package net.schmizz.concurrent;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/*
* Syntactic sugar around Future
*/
/**
* A kind of {@link Future} that caters to boolean values.
* <p/>
* An event can be set, cleared, or awaited, similar to Python's {@code threading.event}. The key difference is that a
* waiter may be delivered an exception of parameterized type {@code T}. Furthermore, an event {@link #isSet()} when it
* is not {@code null} i.e. it can be either {@code true} or {@code false} when set.
*
* @see Future
* waiter may be delivered an exception of parameterized type {@code T}.
* <p/>
* Uses {@link Promise} under the hood.
*/
public class Event<T extends Throwable>
extends Future<Boolean, T> {
public class Event<T extends Throwable> {
private static final Object SOME = new Object() {
@Override
public String toString() {
return "SOME";
}
};
private final Promise<Object, T> promise;
/**
* Creates this event with given {@code name} and exception {@code chainer}. Allocates a new {@link
@@ -42,7 +43,7 @@ public class Event<T extends Throwable>
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
*/
public Event(String name, ExceptionChainer<T> chainer) {
super(name, chainer);
promise = new Promise<Object, T>(name, chainer);
}
/**
@@ -53,12 +54,30 @@ public class Event<T extends Throwable>
* @param lock lock to use
*/
public Event(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
super(name, chainer, lock);
promise = new Promise<Object, T>(name, chainer, lock);
}
/** Sets this event to be {@code true}. Short for {@code set(true)}. */
public void set() {
super.set(true);
promise.deliver(SOME);
}
/** Clear this event. A cleared event {@code !isSet()}. */
public void clear() {
promise.clear();
}
/** Deliver the error {@code t} (after chaining) to any present or future waiters. */
public void deliverError(Throwable t) {
promise.deliverError(t);
}
/**
* @return whether this event is in a 'set' state. An event is set by a call to {@link #set} or {@link
* #deliverError}
*/
public boolean isSet() {
return promise.isDelivered();
}
/**
@@ -68,7 +87,7 @@ public class Event<T extends Throwable>
*/
public void await()
throws T {
super.get();
promise.retrieve();
}
/**
@@ -81,7 +100,47 @@ public class Event<T extends Throwable>
*/
public void await(long timeout, TimeUnit unit)
throws T {
super.get(timeout, unit);
promise.retrieve(timeout, unit);
}
/**
* Await this event to have a definite {@code true} or {@code false} value, for {@code timeout} duration.
* <p/>
* If the definite value is not available when the timeout expires, returns {@code false}.
*
* @param timeout timeout
* @param unit the time unit for the timeout
*
* @throws T if another thread meanwhile informs this event of an error
*/
public boolean tryAwait(long timeout, TimeUnit unit)
throws T {
return promise.tryRetrieve(timeout, unit) != null;
}
/** @return whether there are any threads waiting on this event to be set. */
public boolean hasWaiters() {
return promise.hasWaiters();
}
/** @return whether this event is in an error state i.e. has been delivered an error. */
public boolean inError() {
return promise.inError();
}
/** Acquire the lock associated with this event. */
public void lock() {
promise.lock();
}
/** Release the lock associated with this event. */
public void unlock() {
promise.unlock();
}
@Override
public String toString() {
return promise.toString();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,206 +0,0 @@
/*
* Copyright 2010 Shikhar Bhushan
*
* Licensed 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.concurrent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* Represents future data of the parameterized type {@code V} and allows waiting on it. An exception may also be
* delivered to a waiter, and will be of the parameterized type {@code T}.
* <p/>
* For atomic operations on a future, e.g. checking if a value is set and if it is not then setting it - in other words,
* Compare-And-Set type operations - the associated lock for the future should be acquired while doing so.
*/
public class Future<V, T extends Throwable> {
private final Logger log;
private final ExceptionChainer<T> chainer;
private final ReentrantLock lock;
private final Condition cond;
private V val;
private T pendingEx;
/**
* Creates this future with given {@code name} and exception {@code chainer}. Allocates a new {@link
* java.util.concurrent.locks.Lock lock} object for this future.
*
* @param name name of this future
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
*/
public Future(String name, ExceptionChainer<T> chainer) {
this(name, chainer, null);
}
/**
* Creates this future with given {@code name}, exception {@code chainer}, and associated {@code lock}.
*
* @param name name of this future
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
* @param lock lock to use
*/
public Future(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
this.log = LoggerFactory.getLogger("<< " + name + " >>");
this.chainer = chainer;
this.lock = lock == null ? new ReentrantLock() : lock;
this.cond = this.lock.newCondition();
}
/**
* Set this future's value to {@code val}. Any waiters will be delivered this value.
*
* @param val the value
*/
public void set(V val) {
lock();
try {
log.debug("Setting to `{}`", val);
this.val = val;
cond.signalAll();
} finally {
unlock();
}
}
/**
* Queues error that will be thrown in any waiting thread or any thread that attempts to wait on this future
* hereafter.
*
* @param e the error
*/
public void error(Throwable e) {
lock();
try {
pendingEx = chainer.chain(e);
cond.signalAll();
} finally {
unlock();
}
}
/** Clears this future by setting its value and queued exception to {@code null}. */
public void clear() {
lock();
try {
pendingEx = null;
set(null);
} finally {
unlock();
}
}
/**
* Wait indefinitely for this future's value to be set.
*
* @return the value
*
* @throws T in case another thread informs the future of an error meanwhile
*/
public V get()
throws T {
return get(0, TimeUnit.SECONDS);
}
/**
* Wait for {@code timeout} duration for this future's value to be set.
*
* @param timeout the timeout
* @param unit time unit for the timeout
*
* @return the value
*
* @throws T in case another thread informs the future of an error meanwhile, or the timeout expires
*/
public V get(long timeout, TimeUnit unit)
throws T {
lock();
try {
if (pendingEx != null)
throw pendingEx;
if (val != null)
return val;
log.debug("Awaiting");
while (val == null && pendingEx == null)
if (timeout == 0)
cond.await();
else if (!cond.await(timeout, unit))
throw chainer.chain(new TimeoutException("Timeout expired"));
if (pendingEx != null) {
log.error("Woke to: {}", pendingEx.toString());
throw pendingEx;
}
return val;
} catch (InterruptedException ie) {
throw chainer.chain(ie);
} finally {
unlock();
}
}
/** @return whether this future has a value set, and no error waiting to pop. */
public boolean isSet() {
lock();
try {
return pendingEx == null && val != null;
} finally {
unlock();
}
}
/** @return whether this future currently has an error set. */
public boolean hasError() {
lock();
try {
return pendingEx != null;
} finally {
unlock();
}
}
/** @return whether this future has threads waiting on it. */
public boolean hasWaiters() {
lock();
try {
return lock.hasWaiters(cond);
} finally {
unlock();
}
}
/**
* Lock using the associated lock. Use as part of a {@code try-finally} construct in conjunction with {@link
* #unlock()}.
*/
public void lock() {
lock.lock();
}
/**
* Unlock using the associated lock. Use as part of a {@code try-finally} construct in conjunction with {@link
* #lock()}.
*/
public void unlock() {
lock.unlock();
}
}

View File

@@ -0,0 +1,227 @@
/*
* Copyright 2010, 2011 sshj contributors
*
* Licensed 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.concurrent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* Represents promised data of the parameterized type {@code V} and allows waiting on it. An exception may also be
* delivered to a waiter, and will be of the parameterized type {@code T}.
* <p/>
* For atomic operations on a promise, e.g. checking if a value is delivered and if it is not then setting it, the
* associated lock for the promise should be acquired while doing so.
*/
public class Promise<V, T extends Throwable> {
private final Logger log = LoggerFactory.getLogger(getClass());
private final String name;
private final ExceptionChainer<T> chainer;
private final ReentrantLock lock;
private final Condition cond;
private V val;
private T pendingEx;
/**
* Creates this promise with given {@code name} and exception {@code chainer}. Allocates a new {@link
* java.util.concurrent.locks.Lock lock} object for this promise.
*
* @param name name of this promise
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
*/
public Promise(String name, ExceptionChainer<T> chainer) {
this(name, chainer, null);
}
/**
* Creates this promise with given {@code name}, exception {@code chainer}, and associated {@code lock}.
*
* @param name name of this promise
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
* @param lock lock to use
*/
public Promise(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
this.name = name;
this.chainer = chainer;
this.lock = lock == null ? new ReentrantLock() : lock;
this.cond = this.lock.newCondition();
}
/**
* Set this promise's value to {@code val}. Any waiters will be delivered this value.
*
* @param val the value
*/
public void deliver(V val) {
lock.lock();
try {
log.debug("Setting <<{}>> to `{}`", name, val);
this.val = val;
cond.signalAll();
} finally {
lock.unlock();
}
}
/**
* Queues error that will be thrown in any waiting thread or any thread that attempts to wait on this promise
* hereafter.
*
* @param e the error
*/
public void deliverError(Throwable e) {
lock.lock();
try {
pendingEx = chainer.chain(e);
cond.signalAll();
} finally {
lock.unlock();
}
}
/** Clears this promise by setting its value and queued exception to {@code null}. */
public void clear() {
lock.lock();
try {
pendingEx = null;
deliver(null);
} finally {
lock.unlock();
}
}
/**
* Wait indefinitely for this promise's value to be deliver.
*
* @return the value
*
* @throws T in case another thread informs the promise of an error meanwhile
*/
public V retrieve()
throws T {
return tryRetrieve(0, TimeUnit.SECONDS);
}
/**
* Wait for {@code timeout} duration for this promise's value to be deliver.
*
* @param timeout the timeout
* @param unit time unit for the timeout
*
* @return the value
*
* @throws T in case another thread informs the promise of an error meanwhile, or the timeout expires
*/
public V retrieve(long timeout, TimeUnit unit)
throws T {
final V value = tryRetrieve(timeout, unit);
if (value == null)
throw chainer.chain(new TimeoutException("Timeout expired"));
else
return value;
}
/**
* Wait for {@code timeout} duration for this promise's value to be deliver.
* <p/>
* If the value is not deliver by the time the timeout expires, returns {@code null}.
*
* @param timeout the timeout
* @param unit time unit for the timeout
*
* @return the value or {@code null}
*
* @throws T in case another thread informs the promise of an error meanwhile
*/
public V tryRetrieve(long timeout, TimeUnit unit)
throws T {
lock.lock();
try {
if (pendingEx != null)
throw pendingEx;
if (val != null)
return val;
log.debug("Awaiting <<{}>>", name);
while (val == null && pendingEx == null)
if (timeout == 0)
cond.await();
else if (!cond.await(timeout, unit))
return null;
if (pendingEx != null) {
log.error("<<{}>> woke to: {}", name, pendingEx.toString());
throw pendingEx;
}
return val;
} catch (InterruptedException ie) {
throw chainer.chain(ie);
} finally {
lock.unlock();
}
}
/** @return whether this promise has a value delivered, and no error waiting to pop. */
public boolean isDelivered() {
lock.lock();
try {
return pendingEx == null && val != null;
} finally {
lock.unlock();
}
}
/** @return whether this promise has been delivered an error. */
public boolean inError() {
lock.lock();
try {
return pendingEx != null;
} finally {
lock.unlock();
}
}
/** @return whether this promise has threads waiting on it. */
public boolean hasWaiters() {
lock.lock();
try {
return lock.hasWaiters(cond);
} finally {
lock.unlock();
}
}
/** Acquire the lock associated with this promise. */
public void lock() {
lock.lock();
}
/** Release the lock associated with this promise. */
public void unlock() {
lock.unlock();
}
@Override
public String toString() {
return name;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@ public abstract class AbstractService
@Override
public void notifyError(SSHException error) {
log.debug("Was notified of {}", error.toString());
log.debug("Notified of {}", error.toString());
}
@Override
@@ -66,12 +66,6 @@ public abstract class AbstractService
throw new SSHException(DisconnectReason.PROTOCOL_ERROR, "Unexpected: SSH_MSG_UNIMPLEMENTED");
}
@Override
public void notifyDisconnect()
throws SSHException {
log.debug("Was notified of disconnect");
}
@Override
public void request()
throws TransportException {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,20 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.concurrent;
package net.schmizz.sshj;
import java.util.Collection;
import net.schmizz.sshj.transport.random.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
public class FutureUtils {
public class AndroidConfig
extends DefaultConfig {
public static void alertAll(Throwable x, Future... futures) {
for (Future f : futures)
f.error(x);
}
public static void alertAll(Throwable x, Collection<? extends Future> futures) {
for (Future f : futures)
f.error(x);
@Override
protected void initRandomFactory(boolean ignored) {
setRandomFactory(new SingletonRandomFactory(new JCERandom.Factory()));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ import java.util.List;
* {@link Compression}, {@link MAC}, {@link Signature}, {@link Random}, and {@link FileKeyProvider}.
*/
public interface Config {
/**
* Retrieve the list of named factories for {@code Cipher}.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -91,7 +91,7 @@ public class DefaultConfig
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String VERSION = "SSHJ_0_1";
private static final String VERSION = "SSHJ_0_6_0";
public DefaultConfig() {
setVersion(VERSION);
@@ -134,9 +134,10 @@ public class DefaultConfig
new TripleDESCBC.Factory(),
new BlowfishCBC.Factory()));
boolean warn = false;
// Ref. https://issues.apache.org/jira/browse/SSHD-24
// "AES256 and AES192 requires unlimited cryptography extension"
for (Iterator<Factory.Named<Cipher>> i = avail.iterator(); i.hasNext();) {
for (Iterator<Factory.Named<Cipher>> i = avail.iterator(); i.hasNext(); ) {
final Factory.Named<Cipher> f = i.next();
try {
final Cipher c = f.create();
@@ -144,10 +145,12 @@ public class DefaultConfig
final byte[] iv = new byte[c.getIVSize()];
c.init(Cipher.Mode.Encrypt, key, iv);
} catch (Exception e) {
log.warn("Disabling cipher `{}`: cipher strengths apparently limited by JCE policy", f.getName());
warn = true;
i.remove();
}
}
if (warn)
log.warn("Disabling high-strength ciphers: cipher strengths apparently limited by JCE policy");
setCipherFactories(avail);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
package net.schmizz.sshj;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SecurityUtils;
@@ -33,6 +32,7 @@ import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder.Forward
import net.schmizz.sshj.connection.channel.forwarded.X11Forwarder;
import net.schmizz.sshj.connection.channel.forwarded.X11Forwarder.X11Channel;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.sftp.SFTPEngine;
import net.schmizz.sshj.sftp.StatefulSFTPClient;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
@@ -61,6 +61,7 @@ import net.schmizz.sshj.xfer.scp.SCPFileTransfer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.SocketAddress;
@@ -94,13 +95,15 @@ import java.util.List;
* client = new SSHClient();
* client.initUserKnownHosts();
* client.connect(&quot;hostname&quot;);
* try
* {
* try {
* client.authPassword(&quot;username&quot;, &quot;password&quot;);
* client.startSession().exec(&quot;true&quot;);
* client.getConnection().join();
* } finally
* {
* final Session session = client.startSession();
* try {
* final Command cmd = session.exec(&quot;true&quot;);
* cmd.join(1, TimeUnit.SECONDS);
* } finally {
* session.close();
* } finally {
* client.disconnect();
* }
* </pre>
@@ -110,7 +113,7 @@ import java.util.List;
*/
public class SSHClient
extends SocketClient
implements SessionFactory {
implements Closeable, SessionFactory {
/** Default port for SSH */
public static final int DEFAULT_PORT = 22;
@@ -118,7 +121,6 @@ public class SSHClient
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
/** Transport layer */
protected final Transport trans;
@@ -156,20 +158,18 @@ public class SSHClient
}
/**
* Add a {@link HostKeyVerifier} that will verify any host at given {@code hostname:port} and a host key that has
* the given {@code fingerprint}, e.g. {@code "4b:69:6c:72:6f:79:20:77:61:73:20:68:65:72:65:21"}
* Add a {@link HostKeyVerifier} that will verify any host that's able to claim a host key with the given {@code
* fingerprint}, e.g. {@code "4b:69:6c:72:6f:79:20:77:61:73:20:68:65:72:65:21"}
*
* @param host the hostname / IP address
* @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)
*
* @see SecurityUtils#getFingerprint
*/
public void addHostKeyVerifier(final String host, final int port, final String fingerprint) {
public void addHostKeyVerifier(final String fingerprint) {
addHostKeyVerifier(new HostKeyVerifier() {
@Override
public boolean verify(String h, int p, PublicKey k) {
return host.equals(h) && port == p && SecurityUtils.getFingerprint(k).equals(fingerprint);
return SecurityUtils.getFingerprint(k).equals(fingerprint);
}
});
}
@@ -441,7 +441,7 @@ public class SSHClient
/**
* Utility function for createing a {@link KeyProvider} instance from given location on the file system. Creates a
* one-off {@link PasswordFinder} using {@link PasswordUtils#createOneOff(char[])}, and calls {@link
* #loadKeys(String,PasswordFinder)}.
* #loadKeys(String, PasswordFinder)}.
*
* @param location location of the key file
* @param passphrase passphrase as a char-array
@@ -501,6 +501,33 @@ public class SSHClient
return loadKeys(location, passphrase.toCharArray());
}
/**
* Creates a {@link KeyProvider} instance from passed strings. Currently only PKCS8 format private key files are
* supported (OpenSSH uses this format).
* <p/>
*
* @param privateKey the private key as a string
* @param publicKey the public key as a string if it's not included with the private key
* @param passwordFinder the {@link PasswordFinder} that can supply the passphrase for decryption (may be {@code
* null} in case keyfile is not encrypted)
*
* @return the key provider ready for use in authentication
*
* @throws SSHException if there was no suitable key provider available for the file format; typically because
* BouncyCastle is not in the classpath
* @throws IOException if the key file format is not known, etc.
*/
public KeyProvider loadKeys(String privateKey, String publicKey, PasswordFinder passwordFinder)
throws IOException {
final FileKeyProvider.Format format = KeyProviderUtil.detectKeyFileFormat(privateKey, publicKey != null);
final FileKeyProvider fkp = Factory.Named.Util.create(trans.getConfig().getFileKeyProviderFactories(), format
.toString());
if (fkp == null)
throw new SSHException("No provider available for " + format + " key file");
fkp.init(privateKey, publicKey, passwordFinder);
return fkp;
}
/**
* Attempts loading the user's {@code known_hosts} file from the default locations, i.e. {@code ~/.ssh/known_hosts}
* and {@code ~/.ssh/known_hosts2} on most platforms. Adds the resulting {@link OpenSSHKnownHosts} object as a host
@@ -595,7 +622,7 @@ public class SSHClient
public SFTPClient newSFTPClient()
throws IOException {
assert isConnected() && isAuthenticated();
return new SFTPClient(this);
return new SFTPClient(new SFTPEngine(this).init());
}
/**
@@ -657,15 +684,19 @@ public class SSHClient
assert trans.isRunning();
final long start = System.currentTimeMillis();
try {
trans.doKex();
} catch (TransportException te) {
trans.disconnect(DisconnectReason.KEY_EXCHANGE_FAILED);
throw te;
}
trans.doKex();
log.info("Key exchange took {} seconds", (System.currentTimeMillis() - start) / 1000.0);
}
/**
* Same as {@link #disconnect()}.
*
* @throws IOException
*/
@Override
public void close()
throws IOException {
disconnect();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,7 +48,4 @@ public interface Service
void request()
throws TransportException;
void notifyDisconnect()
throws SSHException;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -32,7 +32,8 @@ public class Base64 {
* @see Base64
* @since 1.3
*/
public static class InputStream extends java.io.FilterInputStream {
public static class InputStream
extends java.io.FilterInputStream {
private final boolean encode; // Encoding or decoding
private int position; // Current position in the buffer
@@ -99,7 +100,8 @@ public class Base64 {
* @since 1.3
*/
@Override
public int read() throws java.io.IOException {
public int read()
throws java.io.IOException {
// Do we need to get data?
if (position < 0)
@@ -197,7 +199,8 @@ public class Base64 {
* @since 1.3
*/
@Override
public int read(byte[] dest, int off, int len) throws java.io.IOException {
public int read(byte[] dest, int off, int len)
throws java.io.IOException {
int i;
int b;
for (i = 0; i < len; i++) {
@@ -222,7 +225,8 @@ public class Base64 {
* @see Base64
* @since 1.3
*/
public static class OutputStream extends java.io.FilterOutputStream {
public static class OutputStream
extends java.io.FilterOutputStream {
private final boolean encode;
private int position;
@@ -289,7 +293,8 @@ public class Base64 {
* @since 1.3
*/
@Override
public void close() throws java.io.IOException {
public void close()
throws java.io.IOException {
// 1. Ensure that pending characters are written
flush();
@@ -308,7 +313,8 @@ public class Base64 {
* @since 2.3
*/
@Override
public void flush() throws java.io.IOException {
public void flush()
throws java.io.IOException {
flushBase64();
super.flush();
}
@@ -318,7 +324,8 @@ public class Base64 {
*
* @throws java.io.IOException if there's an error.
*/
public void flushBase64() throws java.io.IOException {
public void flushBase64()
throws java.io.IOException {
if (position > 0)
if (encode) {
out.write(encode3to4(b4, buffer, position, options));
@@ -346,7 +353,8 @@ public class Base64 {
* @throws java.io.IOException if there's an error flushing
* @since 1.5.1
*/
public void suspendEncoding() throws java.io.IOException {
public void suspendEncoding()
throws java.io.IOException {
flushBase64();
suspendEncoding = true;
} // end suspendEncoding
@@ -361,7 +369,8 @@ public class Base64 {
* @since 1.3
*/
@Override
public void write(byte[] theBytes, int off, int len) throws java.io.IOException {
public void write(byte[] theBytes, int off, int len)
throws java.io.IOException {
// Encoding suspended?
if (suspendEncoding) {
super.out.write(theBytes, off, len);
@@ -383,7 +392,8 @@ public class Base64 {
* @since 1.3
*/
@Override
public void write(int theByte) throws java.io.IOException {
public void write(int theByte)
throws java.io.IOException {
// Encoding suspended?
if (suspendEncoding) {
super.out.write(theByte);
@@ -673,7 +683,8 @@ public class Base64 {
* @throws java.io.IOException If bogus characters exist in source data
* @since 1.3
*/
public static byte[] decode(byte[] source, int off, int len, int options) throws java.io.IOException {
public static byte[] decode(byte[] source, int off, int len, int options)
throws java.io.IOException {
// Lots of error checking and exception throwing
if (source == null)
@@ -725,7 +736,7 @@ public class Base64 {
else
// There's a bad input character in the Base64 stream.
throw new java.io.IOException(String.format("Bad Base64 input character '%c' in array position %d",
source[i], i));
source[i], i));
} // each input character
byte[] out = new byte[outBuffPosn];
@@ -743,7 +754,8 @@ public class Base64 {
* @throws java.io.IOException If there is a problem
* @since 1.4
*/
public static byte[] decode(String s) throws java.io.IOException {
public static byte[] decode(String s)
throws java.io.IOException {
return decode(s, NO_OPTIONS);
}
@@ -759,7 +771,8 @@ public class Base64 {
* @throws NullPointerException if <tt>s</tt> is null
* @since 1.4
*/
public static byte[] decode(String s, int options) throws java.io.IOException {
public static byte[] decode(String s, int options)
throws java.io.IOException {
if (s == null)
throw new NullPointerException("Input string was null.");
@@ -833,7 +846,8 @@ public class Base64 {
* @throws java.io.IOException if there is an error
* @since 2.2
*/
public static void decodeFileToFile(String infile, String outfile) throws java.io.IOException {
public static void decodeFileToFile(String infile, String outfile)
throws java.io.IOException {
byte[] decoded = Base64.decodeFromFile(infile);
java.io.OutputStream out = null;
@@ -864,7 +878,8 @@ public class Base64 {
* @throws java.io.IOException if there is an error
* @since 2.1
*/
public static byte[] decodeFromFile(String filename) throws java.io.IOException {
public static byte[] decodeFromFile(String filename)
throws java.io.IOException {
byte[] decodedData = null;
Base64.InputStream bis = null;
@@ -878,12 +893,12 @@ public class Base64 {
// Check for size of file
if (file.length() > Integer.MAX_VALUE)
throw new java.io.IOException("File is too big for this convenience method (" + file.length()
+ " bytes).");
+ " bytes).");
buffer = new byte[(int) file.length()];
// Open a stream
bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)),
Base64.DECODE);
Base64.DECODE);
// Read until done
while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
@@ -918,7 +933,8 @@ public class Base64 {
* @throws java.io.IOException if there is an error
* @since 2.1
*/
public static void decodeToFile(String dataToDecode, String filename) throws java.io.IOException {
public static void decodeToFile(String dataToDecode, String filename)
throws java.io.IOException {
Base64.OutputStream bos = null;
try {
@@ -950,8 +966,9 @@ public class Base64 {
* @throws ClassNotFoundException if the decoded object is of a class that cannot be found by the JVM
* @since 1.5
*/
public static Object decodeToObject(String encodedObject) throws java.io.IOException,
java.lang.ClassNotFoundException {
public static Object decodeToObject(String encodedObject)
throws java.io.IOException,
java.lang.ClassNotFoundException {
// Decode and gunzip if necessary
byte[] objBytes = decode(encodedObject);
@@ -1078,7 +1095,8 @@ public class Base64 {
* @see Base64#DO_BREAK_LINES
* @since 2.0
*/
public static String encodeBytes(byte[] source, int options) throws java.io.IOException {
public static String encodeBytes(byte[] source, int options)
throws java.io.IOException {
return encodeBytes(source, 0, source.length, options);
} // end encodeBytes
@@ -1137,7 +1155,8 @@ public class Base64 {
* @see Base64#DO_BREAK_LINES
* @since 2.0
*/
public static String encodeBytes(byte[] source, int off, int len, int options) throws java.io.IOException {
public static String encodeBytes(byte[] source, int off, int len, int options)
throws java.io.IOException {
byte[] encoded = encodeBytesToBytes(source, off, len, options);
// Return value according to relevant encoding.
@@ -1189,7 +1208,8 @@ public class Base64 {
* @see Base64#DO_BREAK_LINES
* @since 2.3.1
*/
public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options) throws java.io.IOException {
public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options)
throws java.io.IOException {
if (source == null)
throw new NullPointerException("Cannot serialize a null array.");
@@ -1302,7 +1322,8 @@ public class Base64 {
* @throws java.io.IOException if there is an error
* @since 2.2
*/
public static void encodeFileToFile(String infile, String outfile) throws java.io.IOException {
public static void encodeFileToFile(String infile, String outfile)
throws java.io.IOException {
String encoded = Base64.encodeFromFile(infile);
java.io.OutputStream out = null;
@@ -1333,7 +1354,8 @@ public class Base64 {
* @throws java.io.IOException if there is an error
* @since 2.1
*/
public static String encodeFromFile(String filename) throws java.io.IOException {
public static String encodeFromFile(String filename)
throws java.io.IOException {
String encodedData = null;
Base64.InputStream bis = null;
@@ -1348,7 +1370,7 @@ public class Base64 {
// Open a stream
bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)),
Base64.ENCODE);
Base64.ENCODE);
// Read until done
while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
@@ -1387,7 +1409,8 @@ public class Base64 {
* @throws NullPointerException if serializedObject is null
* @since 1.4
*/
public static String encodeObject(java.io.Serializable serializableObject) throws java.io.IOException {
public static String encodeObject(java.io.Serializable serializableObject)
throws java.io.IOException {
return encodeObject(serializableObject, NO_OPTIONS);
} // end encodeObject
@@ -1420,7 +1443,8 @@ public class Base64 {
* @see Base64#DO_BREAK_LINES
* @since 2.0
*/
public static String encodeObject(java.io.Serializable serializableObject, int options) throws java.io.IOException {
public static String encodeObject(java.io.Serializable serializableObject, int options)
throws java.io.IOException {
if (serializableObject == null)
throw new NullPointerException("Cannot serialize a null object.");
@@ -1481,7 +1505,8 @@ public class Base64 {
* @throws NullPointerException if dataToEncode is null
* @since 2.1
*/
public static void encodeToFile(byte[] dataToEncode, String filename) throws java.io.IOException {
public static void encodeToFile(byte[] dataToEncode, String filename)
throws java.io.IOException {
if (dataToEncode == null)
throw new NullPointerException("Data to encode was null.");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,7 +44,8 @@ import java.util.Arrays;
public class Buffer<T extends Buffer<T>> {
public static class BufferException
extends SSHRuntimeException {
extends SSHException {
public BufferException(String message) {
super(message);
}
@@ -139,7 +140,8 @@ public class Buffer<T extends Buffer<T>> {
this.wpos = wpos;
}
protected void ensureAvailable(int a) {
protected void ensureAvailable(int a)
throws BufferException {
if (available() < a)
throw new BufferException("Underflow");
}
@@ -177,7 +179,8 @@ public class Buffer<T extends Buffer<T>> {
*
* @return the {@code true} or {@code false} value read
*/
public boolean readBoolean() {
public boolean readBoolean()
throws BufferException {
return readByte() != 0;
}
@@ -197,7 +200,8 @@ public class Buffer<T extends Buffer<T>> {
*
* @return the byte read
*/
public byte readByte() {
public byte readByte()
throws BufferException {
ensureAvailable(1);
return data[rpos++];
}
@@ -221,8 +225,9 @@ public class Buffer<T extends Buffer<T>> {
*
* @return the byte-array read
*/
public byte[] readBytes() {
int len = readInt();
public byte[] readBytes()
throws BufferException {
int len = readUInt32AsInt();
if (len < 0 || len > 32768)
throw new BufferException("Bad item length: " + len);
byte[] b = new byte[len];
@@ -251,14 +256,16 @@ public class Buffer<T extends Buffer<T>> {
* @return this
*/
public T putBytes(byte[] b, int off, int len) {
return putInt(len - off).putRawBytes(b, off, len);
return putUInt32(len - off).putRawBytes(b, off, len);
}
public void readRawBytes(byte[] buf) {
public void readRawBytes(byte[] buf)
throws BufferException {
readRawBytes(buf, 0, buf.length);
}
public void readRawBytes(byte[] buf, int off, int len) {
public void readRawBytes(byte[] buf, int off, int len)
throws BufferException {
ensureAvailable(len);
System.arraycopy(data, rpos, buf, off, len);
rpos += len;
@@ -294,16 +301,18 @@ public class Buffer<T extends Buffer<T>> {
return (T) this;
}
public int readInt() {
return (int) readLong();
public int readUInt32AsInt()
throws BufferException {
return (int) readUInt32();
}
public long readLong() {
public long readUInt32()
throws BufferException {
ensureAvailable(4);
return data[rpos++] << 24 & 0xff000000L |
data[rpos++] << 16 & 0x00ff0000L |
data[rpos++] << 8 & 0x0000ff00L |
data[rpos++] & 0x000000ffL;
data[rpos++] << 16 & 0x00ff0000L |
data[rpos++] << 8 & 0x0000ff00L |
data[rpos++] & 0x000000ffL;
}
/**
@@ -314,10 +323,10 @@ public class Buffer<T extends Buffer<T>> {
* @return this
*/
@SuppressWarnings("unchecked")
public T putInt(long uint32) {
public T putUInt32(long uint32) {
ensureCapacity(4);
if (uint32 < 0 || uint32 > 0xffffffffL)
throw new BufferException("Invalid value: " + uint32);
throw new RuntimeException("Invalid value: " + uint32);
data[wpos++] = (byte) (uint32 >> 24);
data[wpos++] = (byte) (uint32 >> 16);
data[wpos++] = (byte) (uint32 >> 8);
@@ -330,54 +339,29 @@ public class Buffer<T extends Buffer<T>> {
*
* @return the MP integer as a {@code BigInteger}
*/
public BigInteger readMPInt() {
return new BigInteger(readMPIntAsBytes());
public BigInteger readMPInt()
throws BufferException {
return new BigInteger(readBytes());
}
/**
* Writes an SSH multiple-precision integer from a {@code BigInteger}
*
* @param bi {@code BigInteger} to write
*
* @return this
*/
public T putMPInt(BigInteger bi) {
return putMPInt(bi.toByteArray());
final byte[] asBytes = bi.toByteArray();
putUInt32(asBytes.length);
return putRawBytes(asBytes);
}
/**
* Writes an SSH multiple-precision integer from a Java byte-array
*
* @param foo byte-array
*
* @return this
*/
public T putMPInt(byte[] foo) {
int i = foo.length;
if ((foo[0] & 0x80) != 0) {
i++;
putInt(i);
putByte((byte) 0);
} else
putInt(i);
return putRawBytes(foo);
}
public byte[] readMPIntAsBytes() {
return readBytes();
}
public long readUINT64() {
long uint64 = (readLong() << 32) + (readLong() & 0xffffffffL);
public long readUInt64()
throws BufferException {
long uint64 = (readUInt32() << 32) + (readUInt32() & 0xffffffffL);
if (uint64 < 0)
throw new BufferException("Cannot handle values > Long.MAX_VALUE");
return uint64;
}
@SuppressWarnings("unchecked")
public T putUINT64(long uint64) {
public T putUInt64(long uint64) {
if (uint64 < 0)
throw new BufferException("Invalid value: " + uint64);
throw new RuntimeException("Invalid value: " + uint64);
data[wpos++] = (byte) (uint64 >> 56);
data[wpos++] = (byte) (uint64 >> 48);
data[wpos++] = (byte) (uint64 >> 40);
@@ -394,8 +378,9 @@ public class Buffer<T extends Buffer<T>> {
*
* @return the string as a Java {@code String}
*/
public String readString() {
int len = readInt();
public String readString()
throws BufferException {
int len = readUInt32AsInt();
if (len < 0 || len > 32768)
throw new BufferException("Bad item length: " + len);
ensureAvailable(len);
@@ -414,7 +399,8 @@ public class Buffer<T extends Buffer<T>> {
*
* @return the string as a byte-array
*/
public byte[] readStringAsBytes() {
public byte[] readStringAsBytes()
throws BufferException {
return readBytes();
}
@@ -427,20 +413,16 @@ public class Buffer<T extends Buffer<T>> {
}
public T putString(String string) {
try {
return putString(string.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new SSHRuntimeException(e);
}
return putString(string.getBytes(IOUtils.UTF8));
}
/**
* Writes a char-array as an SSH string and then blanks it out.
* <p/>
* This is useful when a plaintext password needs to be sent. If {@code passwd} is {@code null}, an empty string is
* This is useful when a plaintext password needs to be sent. If {@code str} is {@code null}, an empty string is
* written.
*
* @param str (null-ok) the password as a character array
* @param str (null-ok) the string as a character array
*
* @return this
*/
@@ -448,7 +430,7 @@ public class Buffer<T extends Buffer<T>> {
public T putSensitiveString(char[] str) {
if (str == null)
return putString("");
putInt(str.length);
putUInt32(str.length);
ensureCapacity(str.length);
for (char c : str)
data[wpos++] = (byte) c;
@@ -456,7 +438,8 @@ public class Buffer<T extends Buffer<T>> {
return (T) this;
}
public PublicKey readPublicKey() {
public PublicKey readPublicKey()
throws BufferException {
try {
final String type = readString();
return KeyType.fromString(type).readPubKeyFromBuffer(type, this);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,25 +35,11 @@
*/
package net.schmizz.sshj.common;
import java.util.Arrays;
/** Utility functions for byte arrays. */
public class ByteArrayUtils {
final static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* Check whether two byte arrays are the equal.
*
* @param a1
* @param a2
*
* @return <code>true</code> or <code>false</code>
*/
public static boolean equals(byte[] a1, byte[] a2) {
return (a1.length != a2.length && equals(a1, 0, a2, 0, a1.length));
}
/**
* Check whether some part or whole of two byte arrays is equal, for <code>length</code> bytes starting at some
* offset.
@@ -75,17 +61,6 @@ public class ByteArrayUtils {
return true;
}
/**
* Get a hexadecimal representation of <code>array</code>, with each octet separated by a space.
*
* @param array
*
* @return hex string, each octet delimited by a space
*/
public static String printHex(byte[] array) {
return printHex(array, 0, array.length);
}
/**
* Get a hexadecimal representation of a byte array starting at <code>offset</code> index for <code>len</code>
* bytes, with each octet separated by a space.
@@ -139,8 +114,4 @@ public class ByteArrayUtils {
return sb.toString();
}
public static byte[] copyOf(byte[] array) {
return Arrays.copyOf(array, array.length);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,39 +18,32 @@ package net.schmizz.sshj.common;
/** Disconnect error codes */
public enum DisconnectReason {
UNKNOWN(0),
HOST_NOT_ALLOWED_TO_CONNECT(1),
PROTOCOL_ERROR(2),
KEY_EXCHANGE_FAILED(3),
HOST_AUTHENTICATION_FAILED(4),
RESERVED(4),
MAC_ERROR(5),
COMPRESSION_ERROR(6),
SERVICE_NOT_AVAILABLE(7),
PROTOCOL_VERSION_NOT_SUPPORTED(8),
HOST_KEY_NOT_VERIFIABLE(9),
CONNECTION_LOST(10),
BY_APPLICATION(11),
TOO_MANY_CONNECTIONS(12),
AUTH_CANCELLED_BY_USER(13),
NO_MORE_AUTH_METHODS_AVAILABLE(14),
ILLEGAL_USER_NAME(15);
UNKNOWN,
HOST_NOT_ALLOWED_TO_CONNECT,
PROTOCOL_ERROR,
KEY_EXCHANGE_FAILED,
RESERVED,
MAC_ERROR,
COMPRESSION_ERROR,
SERVICE_NOT_AVAILABLE,
PROTOCOL_VERSION_NOT_SUPPORTED,
HOST_KEY_NOT_VERIFIABLE,
CONNECTION_LOST,
BY_APPLICATION,
TOO_MANY_CONNECTIONS,
AUTH_CANCELLED_BY_USER,
NO_MORE_AUTH_METHODS_AVAILABLE,
ILLEGAL_USER_NAME;
public static DisconnectReason fromInt(int code) {
for (DisconnectReason dc : values())
if (dc.code == code)
return dc;
return UNKNOWN;
}
private final int code;
private DisconnectReason(int code) {
this.code = code;
final int len = values().length;
if (code < 0 || code > len)
return UNKNOWN;
return values()[code];
}
public int toInt() {
return code;
return ordinal();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ public interface ErrorNotifiable {
/** Utility functions. */
class Util {
/** Notify all {@code notifiables} of given {@code error}. */
public static void alertAll(SSHException error, ErrorNotifiable... notifiables) {
for (ErrorNotifiable notifiable : notifiables)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,13 +38,18 @@ package net.schmizz.sshj.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
public class IOUtils {
private static final Logger LOG = LoggerFactory.getLogger(IOUtils.class);
public static final Charset UTF8 = Charset.forName("UTF-8");
public static void closeQuietly(Closeable... closeables) {
for (Closeable c : closeables)
try {
@@ -55,4 +60,11 @@ public class IOUtils {
}
}
}
public static ByteArrayOutputStream readFully(InputStream stream)
throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
new StreamCopier(stream, baos).copy();
return baos;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,24 +32,28 @@ public enum KeyType {
/** SSH identifier for RSA keys */
RSA("ssh-rsa") {
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
throws GeneralSecurityException {
final BigInteger e = buf.readMPInt();
final BigInteger n = buf.readMPInt();
final BigInteger e, n;
try {
e = buf.readMPInt();
n = buf.readMPInt();
} catch (Buffer.BufferException be) {
throw new GeneralSecurityException(be);
}
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
return keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
}
@Override
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
final RSAPublicKey rsaKey = (RSAPublicKey) pk;
buf.putString(sType)
.putMPInt(rsaKey.getPublicExponent()) // e
.putMPInt(rsaKey.getModulus()); // n
}
@Override
protected boolean isMyType(Key key) {
return (key instanceof RSAPublicKey || key instanceof RSAPrivateKey);
@@ -59,17 +63,22 @@ public enum KeyType {
/** SSH identifier for DSA keys */
DSA("ssh-dss") {
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
throws GeneralSecurityException {
final BigInteger p = buf.readMPInt();
final BigInteger q = buf.readMPInt();
final BigInteger g = buf.readMPInt();
final BigInteger y = buf.readMPInt();
BigInteger p, q, g, y;
try {
p = buf.readMPInt();
q = buf.readMPInt();
g = buf.readMPInt();
y = buf.readMPInt();
} catch (Buffer.BufferException be) {
throw new GeneralSecurityException(be);
}
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
return keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
}
@Override
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
final DSAPublicKey dsaKey = (DSAPublicKey) pk;
@@ -89,7 +98,6 @@ public enum KeyType {
/** Unrecognized */
UNKNOWN("unknown") {
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
throws GeneralSecurityException {
@@ -120,7 +128,6 @@ public enum KeyType {
public abstract void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
protected abstract boolean isMyType(Key key);
public static KeyType fromKey(Key key) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package net.schmizz.sshj.common;
/** SSH message identifiers */
public enum Message {
UNKNOWN(0),
DISCONNECT(1),
IGNORE(2),
UNIMPLEMENTED(3),
@@ -67,8 +68,11 @@ public enum Message {
static {
for (Message c : Message.values())
if (cache[c.toByte()] == null)
cache[c.toByte()] = c;
cache[c.toByte()] = c;
for (int i = 0; i < 256; i++) {
if (cache[i] == null)
cache[i] = UNKNOWN;
}
}
public static Message fromByte(byte b) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -75,12 +75,9 @@ public class SSHPacket
*
* @return the message identifier
*/
public Message readMessageID() {
byte b = readByte();
Message cmd = Message.fromByte(b);
if (cmd == null)
throw new BufferException("Unknown message ID: " + b);
return cmd;
public Message readMessageID()
throws BufferException {
return Message.fromByte(readByte());
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,6 +58,7 @@ import java.security.Signature;
public class SecurityUtils {
private static class BouncyCastleRegistration {
public void run()
throws Exception {
if (java.security.Security.getProvider(BOUNCY_CASTLE) == null) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,129 +15,143 @@
*/
package net.schmizz.sshj.common;
import net.schmizz.concurrent.Event;
import net.schmizz.concurrent.ExceptionChainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class StreamCopier
extends Thread {
private static final Logger LOG = LoggerFactory.getLogger(StreamCopier.class);
public interface ErrorCallback {
void onError(IOException ioe);
}
public static ErrorCallback closeOnErrorCallback(final Closeable... toClose) {
return new ErrorCallback() {
@Override
public void onError(IOException ioe) {
IOUtils.closeQuietly(toClose);
}
};
}
public class StreamCopier {
public interface Listener {
void reportProgress(long transferred);
void reportProgress(long transferred)
throws IOException;
}
public static long copy(InputStream in, OutputStream out, int bufSize, boolean keepFlushing, Listener listener)
throws IOException {
long count = 0;
final boolean reportProgress = listener != null;
final long startTime = System.currentTimeMillis();
final byte[] buf = new byte[bufSize];
int read;
while ((read = in.read(buf)) != -1) {
out.write(buf, 0, read);
count += read;
if (keepFlushing)
out.flush();
if (reportProgress)
listener.reportProgress(count);
private static final Listener NULL_LISTENER = new Listener() {
@Override
public void reportProgress(long transferred) {
}
if (!keepFlushing)
out.flush();
};
final double sizeKiB = count / 1024.0;
final double timeSeconds = (System.currentTimeMillis() - startTime) / 1000.0;
LOG.info(sizeKiB + " KiB transferred in {} seconds ({} KiB/s)", timeSeconds, (sizeKiB / timeSeconds));
return count;
}
public static long copy(InputStream in, OutputStream out, int bufSize, boolean keepFlushing)
throws IOException {
return copy(in, out, bufSize, keepFlushing, null);
}
public static String copyStreamToString(InputStream stream)
throws IOException {
final StringBuilder sb = new StringBuilder();
int read;
while ((read = stream.read()) != -1)
sb.append((char) read);
return sb.toString();
}
private final Logger log;
private final Logger log = LoggerFactory.getLogger(getClass());
private final InputStream in;
private final OutputStream out;
private Listener listener = NULL_LISTENER;
private int bufSize = 1;
private boolean keepFlushing = true;
private long length = -1;
private ErrorCallback errCB = new ErrorCallback() {
@Override
public void onError(IOException ioe) {
}
}; // Default null cb
public StreamCopier(String name, InputStream in, OutputStream out) {
public StreamCopier(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
setName("streamCopier");
log = LoggerFactory.getLogger(name);
}
public StreamCopier bufSize(int size) {
bufSize = size;
public StreamCopier bufSize(int bufSize) {
this.bufSize = bufSize;
return this;
}
public StreamCopier keepFlushing(boolean choice) {
keepFlushing = choice;
public StreamCopier keepFlushing(boolean keepFlushing) {
this.keepFlushing = keepFlushing;
return this;
}
public StreamCopier daemon(boolean choice) {
setDaemon(choice);
public StreamCopier listener(Listener listener) {
if (listener == null) listener = NULL_LISTENER;
this.listener = listener;
return this;
}
public StreamCopier errorCallback(ErrorCallback errCB) {
this.errCB = errCB;
public StreamCopier length(long length) {
this.length = length;
return this;
}
@Override
public void run() {
try {
log.debug("Wil pipe from {} to {}", in, out);
copy(in, out, bufSize, keepFlushing);
log.debug("EOF on {}", in);
} catch (IOException ioe) {
log.error("In pipe from {} to {}: " + ioe.toString(), in, out);
errCB.onError(ioe);
public Event<IOException> spawn(String name) {
return spawn(name, false);
}
public Event<IOException> spawnDaemon(String name) {
return spawn(name, true);
}
private Event<IOException> spawn(final String name, final boolean daemon) {
final Event<IOException> doneEvent =
new Event<IOException>("copyDone", new ExceptionChainer<IOException>() {
@Override
public IOException chain(Throwable t) {
return (t instanceof IOException) ? (IOException) t : new IOException(t);
}
});
new Thread() {
{
setName(name);
setDaemon(daemon);
}
@Override
public void run() {
try {
log.debug("Will copy from {} to {}", in, out);
copy();
log.debug("Done copying from {}", in);
doneEvent.set();
} catch (IOException ioe) {
log.error("In pipe from {} to {}: " + ioe.toString(), in, out);
doneEvent.deliverError(ioe);
}
}
}.start();
return doneEvent;
}
public long copy()
throws IOException {
final byte[] buf = new byte[bufSize];
long count = 0;
int read = 0;
final long startTime = System.currentTimeMillis();
if (length == -1) {
while ((read = in.read(buf)) != -1)
count = write(buf, count, read);
} else {
while (count < length && (read = in.read(buf, 0, (int) Math.min(bufSize, length - count))) != -1)
count = write(buf, count, read);
}
if (!keepFlushing)
out.flush();
final double timeSeconds = (System.currentTimeMillis() - startTime) / 1000.0;
final double sizeKiB = count / 1024.0;
log.info(sizeKiB + " KiB transferred in {} seconds ({} KiB/s)", timeSeconds, (sizeKiB / timeSeconds));
if (length != -1 && read == -1)
throw new IOException("Encountered EOF, could not transfer " + length + " bytes");
return count;
}
private long write(byte[] buf, long count, int read)
throws IOException {
out.write(buf, 0, read);
count += read;
if (keepFlushing)
out.flush();
listener.reportProgress(count);
return count;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
*/
package net.schmizz.sshj.connection;
import net.schmizz.concurrent.Future;
import net.schmizz.concurrent.Promise;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.connection.channel.OpenFailException;
@@ -89,13 +89,13 @@ public interface Connection {
* @param wantReply whether a reply is requested
* @param specifics {@link SSHPacket} containing fields specific to the request
*
* @return a {@link Future} for the reply data (in case {@code wantReply} is true) which allows waiting on the
* reply, or {@code null} if a reply is not requested.
* @return a {@link net.schmizz.concurrent.Promise} for the reply data (in case {@code wantReply} is true) which
* allows waiting on the reply, or {@code null} if a reply is not requested.
*
* @throws TransportException if there is an error sending the request
*/
public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
byte[] specifics)
public Promise<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
byte[] specifics)
throws TransportException;
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,16 +15,16 @@
*/
package net.schmizz.sshj.connection;
import net.schmizz.concurrent.Future;
import net.schmizz.concurrent.FutureUtils;
import net.schmizz.concurrent.ErrorDeliveryUtil;
import net.schmizz.concurrent.Promise;
import net.schmizz.sshj.AbstractService;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.ErrorNotifiable;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.connection.channel.OpenFailException;
import net.schmizz.sshj.connection.channel.OpenFailException.Reason;
import net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener;
import net.schmizz.sshj.transport.Transport;
@@ -49,7 +49,7 @@ public class ConnectionImpl
private final Map<String, ForwardedChannelOpener> openers = new ConcurrentHashMap<String, ForwardedChannelOpener>();
private final Queue<Future<SSHPacket, ConnectionException>> globalReqFutures = new LinkedList<Future<SSHPacket, ConnectionException>>();
private final Queue<Promise<SSHPacket, ConnectionException>> globalReqPromises = new LinkedList<Promise<SSHPacket, ConnectionException>>();
private int windowSize = 2048 * 1024;
private int maxPacketSize = 32 * 1024;
@@ -103,14 +103,18 @@ public class ConnectionImpl
private Channel getChannel(SSHPacket buffer)
throws ConnectionException {
int recipient = buffer.readInt();
Channel channel = get(recipient);
if (channel != null)
return channel;
else {
buffer.rpos(buffer.rpos() - 5);
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Received " + buffer.readMessageID()
+ " on unknown channel #" + recipient);
try {
final int recipient = buffer.readUInt32AsInt();
final Channel channel = get(recipient);
if (channel != null)
return channel;
else {
buffer.rpos(buffer.rpos() - 5);
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
"Received " + buffer.readMessageID() + " on unknown channel #" + recipient);
}
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
}
@@ -139,19 +143,6 @@ public class ConnectionImpl
super.handle(msg, buf);
}
@Override
public void notifyError(SSHException error) {
super.notifyError(error);
synchronized (globalReqFutures) {
FutureUtils.alertAll(error, globalReqFutures);
globalReqFutures.clear();
}
ErrorNotifiable.Util.alertAll(error, channels.values());
channels.clear();
}
@Override
public int getMaxPacketSize() {
return maxPacketSize;
@@ -192,46 +183,51 @@ public class ConnectionImpl
}
@Override
public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
byte[] specifics)
public Promise<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
byte[] specifics)
throws TransportException {
synchronized (globalReqFutures) {
synchronized (globalReqPromises) {
log.info("Making global request for `{}`", name);
trans.write(new SSHPacket(Message.GLOBAL_REQUEST).putString(name)
.putBoolean(wantReply).putRawBytes(specifics));
.putBoolean(wantReply)
.putRawBytes(specifics));
Future<SSHPacket, ConnectionException> future = null;
Promise<SSHPacket, ConnectionException> promise = null;
if (wantReply) {
future = new Future<SSHPacket, ConnectionException>("global req for " + name, ConnectionException.chainer);
globalReqFutures.add(future);
promise = new Promise<SSHPacket, ConnectionException>("global req for " + name, ConnectionException.chainer);
globalReqPromises.add(promise);
}
return future;
return promise;
}
}
private void gotGlobalReqResponse(SSHPacket response)
throws ConnectionException {
synchronized (globalReqFutures) {
Future<SSHPacket, ConnectionException> gr = globalReqFutures.poll();
synchronized (globalReqPromises) {
Promise<SSHPacket, ConnectionException> gr = globalReqPromises.poll();
if (gr == null)
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
"Got a global request response when none was requested");
else if (response == null)
gr.error(new ConnectionException("Global request [" + gr + "] failed"));
gr.deliverError(new ConnectionException("Global request [" + gr + "] failed"));
else
gr.set(response);
gr.deliver(response);
}
}
private void gotChannelOpen(SSHPacket buf)
throws ConnectionException, TransportException {
final String type = buf.readString();
log.debug("Received CHANNEL_OPEN for `{}` channel", type);
if (openers.containsKey(type))
openers.get(type).handleOpen(buf);
else {
log.warn("No opener found for `{}` CHANNEL_OPEN request -- rejecting", type);
sendOpenFailure(buf.readInt(), OpenFailException.Reason.UNKNOWN_CHANNEL_TYPE, "");
try {
final String type = buf.readString();
log.debug("Received CHANNEL_OPEN for `{}` channel", type);
if (openers.containsKey(type))
openers.get(type).handleOpen(buf);
else {
log.warn("No opener found for `{}` CHANNEL_OPEN request -- rejecting", type);
sendOpenFailure(buf.readUInt32AsInt(), Reason.UNKNOWN_CHANNEL_TYPE, "");
}
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
}
@@ -239,18 +235,20 @@ public class ConnectionImpl
public void sendOpenFailure(int recipient, Reason reason, String message)
throws TransportException {
trans.write(new SSHPacket(Message.CHANNEL_OPEN_FAILURE)
.putInt(recipient)
.putInt(reason.getCode())
.putString(message));
.putUInt32(recipient)
.putUInt32(reason.getCode())
.putString(message));
}
@Override
public void notifyDisconnect()
throws SSHException {
super.notifyDisconnect();
FutureUtils.alertAll(new ConnectionException("Disconnected."), globalReqFutures);
for (Channel chan : channels.values())
chan.close();
public void notifyError(SSHException error) {
super.notifyError(error);
synchronized (globalReqPromises) {
ErrorDeliveryUtil.alertPromises(error, globalReqPromises);
globalReqPromises.clear();
}
ErrorNotifiable.Util.alertAll(error, channels.values());
channels.clear();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,8 +35,8 @@
*/
package net.schmizz.sshj.connection.channel;
import net.schmizz.concurrent.ErrorDeliveryUtil;
import net.schmizz.concurrent.Event;
import net.schmizz.concurrent.FutureUtils;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.ByteArrayUtils;
import net.schmizz.sshj.common.DisconnectReason;
@@ -62,7 +62,7 @@ public abstract class AbstractChannel
implements Channel {
/** Logger */
protected final Logger log;
protected final Logger log = LoggerFactory.getLogger(getClass());
/** Transport layer */
protected final Transport trans;
@@ -83,7 +83,7 @@ public abstract class AbstractChannel
/** Channel open event */
protected final Event<ConnectionException> open;
/** Channel close event */
private final Event<ConnectionException> close;
protected final Event<ConnectionException> close;
/* Access to these fields should be synchronized using this object */
private boolean eofSent;
@@ -109,9 +109,7 @@ public abstract class AbstractChannel
id = conn.nextID();
log = LoggerFactory.getLogger("chan#" + id);
lwin = new Window.Local(id, conn.getWindowSize(), conn.getMaxPacketSize());
lwin = new Window.Local(conn.getWindowSize(), conn.getMaxPacketSize());
in = new ChannelInputStream(this, trans, lwin);
open = new Event<ConnectionException>("chan#" + id + " / " + "open", ConnectionException.chainer, lock);
@@ -120,7 +118,7 @@ public abstract class AbstractChannel
protected void init(int recipient, int remoteWinSize, int remoteMaxPacketSize) {
this.recipient = recipient;
rwin = new Window.Remote(id, remoteWinSize, remoteMaxPacketSize);
rwin = new Window.Remote(remoteWinSize, remoteMaxPacketSize);
out = new ChannelOutputStream(this, trans, rwin);
log.info("Initialized - {}", this);
}
@@ -185,11 +183,11 @@ public abstract class AbstractChannel
break;
case CHANNEL_EXTENDED_DATA:
gotExtendedData(buf.readInt(), buf);
gotExtendedData(buf);
break;
case CHANNEL_WINDOW_ADJUST:
gotWindowAdjustment(buf.readInt());
gotWindowAdjustment(buf);
break;
case CHANNEL_REQUEST:
@@ -238,11 +236,12 @@ public abstract class AbstractChannel
public void notifyError(SSHException error) {
log.debug("Channel #{} got notified of {}", getID(), error.toString());
FutureUtils.alertAll(error, open, close);
FutureUtils.alertAll(error, chanReqResponseEvents);
ErrorDeliveryUtil.alertEvents(error, open, close);
ErrorDeliveryUtil.alertEvents(error, chanReqResponseEvents);
in.notifyError(error);
out.notifyError(error);
if (out != null)
out.notifyError(error);
finishOff();
}
@@ -260,7 +259,7 @@ public abstract class AbstractChannel
try {
sendClose();
} catch (TransportException e) {
if (!close.hasError())
if (!close.inError())
throw e;
}
close.await(conn.getTimeout(), TimeUnit.SECONDS);
@@ -269,6 +268,16 @@ public abstract class AbstractChannel
}
}
public void join()
throws ConnectionException {
close.await();
}
public void join(int timeout, TimeUnit unit)
throws ConnectionException {
close.await(timeout, unit);
}
protected synchronized void sendClose()
throws TransportException {
try {
@@ -293,27 +302,38 @@ public abstract class AbstractChannel
private void gotChannelRequest(SSHPacket buf)
throws ConnectionException, TransportException {
final String reqType = buf.readString();
buf.readBoolean(); // We don't care about the 'want-reply' value
final String reqType;
try {
reqType = buf.readString();
buf.readBoolean(); // We don't care about the 'want-reply' value
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
log.info("Got chan request for `{}`", reqType);
handleRequest(reqType, buf);
}
private void gotWindowAdjustment(int howMuch) {
private void gotWindowAdjustment(SSHPacket buf)
throws ConnectionException {
final int howMuch;
try {
howMuch = buf.readUInt32AsInt();
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
log.info("Received window adjustment for {} bytes", howMuch);
rwin.expand(howMuch);
}
/** Called when this channel's end-of-life has been reached. Subclasses may override but must call super. */
protected void finishOff() {
conn.forget(this);
close.set();
}
protected void gotExtendedData(int dataTypeCode, SSHPacket buf)
protected void gotExtendedData(SSHPacket buf)
throws ConnectionException, TransportException {
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Extended data not supported on " + type
+ " channel");
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
"Extended data not supported on " + type + " channel");
}
protected void gotUnknown(Message msg, SSHPacket buf)
@@ -326,50 +346,59 @@ public abstract class AbstractChannel
}
protected SSHPacket newBuffer(Message cmd) {
return new SSHPacket(cmd).putInt(recipient);
return new SSHPacket(cmd).putUInt32(recipient);
}
protected void receiveInto(ChannelInputStream stream, SSHPacket buf)
throws ConnectionException, TransportException {
final int len = buf.readInt();
if (len < 0 || len > getLocalMaxPacketSize() || len != buf.available())
final int len;
try {
len = buf.readUInt32AsInt();
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
if (len < 0 || len > getLocalMaxPacketSize() || len > buf.available())
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR, "Bad item length: " + len);
if (log.isTraceEnabled())
log.trace("IN #{}: {}", id, ByteArrayUtils.printHex(buf.array(), buf.rpos(), len));
stream.receive(buf.array(), buf.rpos(), len);
}
protected synchronized Event<ConnectionException> sendChannelRequest(String reqType, boolean wantReply,
Buffer.PlainBuffer reqSpecific)
protected Event<ConnectionException> sendChannelRequest(String reqType, boolean wantReply,
Buffer.PlainBuffer reqSpecific)
throws TransportException {
log.info("Sending channel request for `{}`", reqType);
trans.write(
newBuffer(Message.CHANNEL_REQUEST)
.putString(reqType)
.putBoolean(wantReply)
.putBuffer(reqSpecific)
);
synchronized (chanReqResponseEvents) {
trans.write(
newBuffer(Message.CHANNEL_REQUEST)
.putString(reqType)
.putBoolean(wantReply)
.putBuffer(reqSpecific)
);
Event<ConnectionException> responseEvent = null;
if (wantReply) {
responseEvent = new Event<ConnectionException>("chan#" + id + " / " + "chanreq for " + reqType, ConnectionException.chainer, lock);
chanReqResponseEvents.add(responseEvent);
Event<ConnectionException> responseEvent = null;
if (wantReply) {
responseEvent = new Event<ConnectionException>("chan#" + id + " / " + "chanreq for " + reqType,
ConnectionException.chainer);
chanReqResponseEvents.add(responseEvent);
}
return responseEvent;
}
return responseEvent;
}
private synchronized void gotResponse(boolean success)
private void gotResponse(boolean success)
throws ConnectionException {
final Event<ConnectionException> responseEvent = chanReqResponseEvents.poll();
if (responseEvent != null) {
if (success)
responseEvent.set();
else
responseEvent.error(new ConnectionException("Request failed"));
} else
throw new ConnectionException(
DisconnectReason.PROTOCOL_ERROR,
"Received response to channel request when none was requested");
synchronized (chanReqResponseEvents) {
final Event<ConnectionException> responseEvent = chanReqResponseEvents.poll();
if (responseEvent != null) {
if (success)
responseEvent.set();
else
responseEvent.deliverError(new ConnectionException("Request failed"));
} else
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
"Received response to channel request when none was requested");
}
}
private synchronized void gotEOF()
@@ -405,7 +434,7 @@ public abstract class AbstractChannel
@Override
public String toString() {
return "< " + type + " channel: id=" + id + ", recipient=" + recipient + ", localWin=" + lwin + ", remoteWin="
+ rwin + " >";
+ rwin + " >";
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import net.schmizz.sshj.transport.TransportException;
import java.io.Closeable;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
/** A channel is the basic medium for application-layer data on top of an SSH transport. */
public interface Channel
@@ -31,6 +32,7 @@ public interface Channel
/** Direct channels are those that are initiated by us. */
interface Direct
extends Channel {
/**
* Request opening this channel from remote end.
*
@@ -135,4 +137,10 @@ public interface Channel
*/
void setAutoExpand(boolean autoExpand);
void join()
throws ConnectionException;
void join(int timeout, TimeUnit unit)
throws ConnectionException;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@ public final class ChannelInputStream
extends InputStream
implements ErrorNotifiable {
private final Logger log;
private final Logger log = LoggerFactory.getLogger(getClass());
private final Channel chan;
private final Transport trans;
@@ -71,8 +71,6 @@ public final class ChannelInputStream
private SSHException error;
public ChannelInputStream(Channel chan, Transport trans, Window.Local win) {
log = LoggerFactory.getLogger("<< chan#" + chan.getID() + " / input stream >>");
this.chan = chan;
this.trans = trans;
this.win = win;
@@ -118,7 +116,7 @@ public final class ChannelInputStream
public int read(byte[] b, int off, int len)
throws IOException {
synchronized (buf) {
for (; ;) {
for (; ; ) {
if (buf.available() > 0)
break;
if (eof)
@@ -165,7 +163,7 @@ public final class ChannelInputStream
if (adjustment > 0) {
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));
.putUInt32(chan.getRecipient()).putUInt32(adjustment));
win.expand(adjustment);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -74,8 +74,8 @@ public final class ChannelOutputStream
buffer.rpos(5);
buffer.wpos(5);
buffer.putMessageID(Message.CHANNEL_DATA);
buffer.putInt(0); // meant to be recipient
buffer.putInt(0); // meant to be data length
buffer.putUInt32(0); // meant to be recipient
buffer.putUInt32(0); // meant to be data length
}
@Override
@@ -153,8 +153,8 @@ public final class ChannelOutputStream
private void putRecipientAndLength() {
final int origPos = buffer.wpos();
buffer.wpos(6);
buffer.putInt(chan.getRecipient());
buffer.putInt(bufferLength);
buffer.putUInt32(chan.getRecipient());
buffer.putUInt32(bufferLength);
buffer.wpos(origPos);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2010, 2011 sshj contributors
*
* Licensed 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;
import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.IOUtils;
import java.io.Closeable;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
public class SocketStreamCopyMonitor
extends Thread {
private SocketStreamCopyMonitor(Runnable r) {
super(r);
setName("sockmon");
setDaemon(true);
}
private static Closeable wrapSocket(final Socket socket) {
return new Closeable() {
@Override
public void close()
throws IOException {
socket.close();
}
};
}
public static void monitor(final int frequency, final TimeUnit unit,
final Event<IOException> x, final Event<IOException> y,
final Channel channel, final Socket socket) {
new SocketStreamCopyMonitor(new Runnable() {
public void run() {
try {
for (Event<IOException> ev = x;
!ev.tryAwait(frequency, unit);
ev = (ev == x) ? y : x) {
}
} catch (IOException ignored) {
} finally {
IOUtils.closeQuietly(channel, wrapSocket(socket));
}
}
}).start();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@ import org.slf4j.LoggerFactory;
public abstract class Window {
protected final Logger log;
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final Object lock = new Object();
@@ -30,8 +30,7 @@ public abstract class Window {
protected int size;
public Window(int chanID, String kindOfWindow, int initialWinSize, int maxPacketSize) {
log = LoggerFactory.getLogger("<< chan#" + chanID + " / " + kindOfWindow + " >>");
public Window(int initialWinSize, int maxPacketSize) {
size = initialWinSize;
this.maxPacketSize = maxPacketSize;
}
@@ -52,12 +51,13 @@ public abstract class Window {
return size;
}
public void consume(int dec) {
public void consume(int dec)
throws ConnectionException {
synchronized (lock) {
log.debug("Consuming by " + dec + " down to " + size);
size -= dec;
if (size < 0)
throw new SSHRuntimeException("Window consumed to below 0");
throw new ConnectionException("Window consumed to below 0");
}
}
@@ -70,8 +70,8 @@ public abstract class Window {
public static final class Remote
extends Window {
public Remote(int chanID, int initialWinSize, int maxPacketSize) {
super(chanID, "remote win", initialWinSize, maxPacketSize);
public Remote(int initialWinSize, int maxPacketSize) {
super(initialWinSize, maxPacketSize);
}
public void waitAndConsume(int howMuch)
@@ -89,6 +89,14 @@ public abstract class Window {
}
}
public void consume(int howMuch) {
try {
super.consume(howMuch);
} catch (ConnectionException e) {
throw new SSHRuntimeException(e);
}
}
}
/** Controls how much data remote end can send before an adjustment notification from us is required. */
@@ -98,8 +106,8 @@ public abstract class Window {
private final int initialSize;
private final int threshold;
public Local(int chanID, int initialWinSize, int maxPacketSize) {
super(chanID, "local win", initialWinSize, maxPacketSize);
public Local(int initialWinSize, int maxPacketSize) {
super(initialWinSize, maxPacketSize);
this.initialSize = initialWinSize;
threshold = Math.min(maxPacketSize * 20, initialSize / 4);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
*/
package net.schmizz.sshj.connection.channel.direct;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.connection.Connection;
@@ -67,22 +68,32 @@ public abstract class AbstractDirectChannel
open.await(conn.getTimeout(), TimeUnit.SECONDS);
}
private void gotOpenConfirmation(SSHPacket buf) {
init(buf.readInt(), buf.readInt(), buf.readInt());
private void gotOpenConfirmation(SSHPacket buf)
throws ConnectionException {
try {
init(buf.readUInt32AsInt(), buf.readUInt32AsInt(), buf.readUInt32AsInt());
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
open.set();
}
private void gotOpenFailure(SSHPacket buf) {
open.error(new OpenFailException(getType(), buf.readInt(), buf.readString()));
private void gotOpenFailure(SSHPacket buf)
throws ConnectionException {
try {
open.deliverError(new OpenFailException(getType(), buf.readUInt32AsInt(), buf.readString()));
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
finishOff();
}
protected SSHPacket buildOpenReq() {
return new SSHPacket(Message.CHANNEL_OPEN)
.putString(getType())
.putInt(getID())
.putInt(getLocalWinSize())
.putInt(getLocalMaxPacketSize());
.putUInt32(getID())
.putUInt32(getLocalWinSize())
.putUInt32(getLocalMaxPacketSize());
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,19 +15,20 @@
*/
package net.schmizz.sshj.connection.channel.direct;
import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.common.StreamCopier.ErrorCallback;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ServerSocketFactory;
import java.io.Closeable;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;
public class LocalPortForwarder {
@@ -45,36 +46,22 @@ public class LocalPortForwarder {
throws IOException {
sock.setSendBufferSize(getLocalMaxPacketSize());
sock.setReceiveBufferSize(getRemoteMaxPacketSize());
final ErrorCallback closer = StreamCopier.closeOnErrorCallback(this,
new Closeable() {
@Override
public void close()
throws IOException {
sock.close();
}
});
new StreamCopier("chan2soc", getInputStream(), sock.getOutputStream())
.bufSize(getLocalMaxPacketSize())
.errorCallback(closer)
.daemon(true)
.start();
new StreamCopier("soc2chan", sock.getInputStream(), getOutputStream())
final Event<IOException> soc2chan = new StreamCopier(sock.getInputStream(), getOutputStream())
.bufSize(getRemoteMaxPacketSize())
.errorCallback(closer)
.daemon(true)
.start();
.spawnDaemon("soc2chan");
final Event<IOException> chan2soc = new StreamCopier(getInputStream(), sock.getOutputStream())
.bufSize(getLocalMaxPacketSize())
.spawnDaemon("chan2soc");
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, sock);
}
@Override
protected SSHPacket buildOpenReq() {
return super.buildOpenReq()
.putString(host)
.putInt(port)
.putString(ss.getInetAddress().getHostAddress())
.putInt(ss.getLocalPort());
.putString(host)
.putUInt32(port)
.putString(ss.getInetAddress().getHostAddress())
.putUInt32(ss.getLocalPort());
}
}
@@ -148,6 +135,7 @@ public class LocalPortForwarder {
chan.open();
chan.start();
}
log.info("Interrupted!");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -140,7 +140,7 @@ public enum PTYMode {
Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
for (Entry<PTYMode, Integer> entry : modes.entrySet()) {
buf.putByte(entry.getKey().getOpcode());
buf.putInt(entry.getValue());
buf.putUInt32(entry.getValue());
}
buf.putByte((byte) 0);
return buf.getCompactData();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,16 +43,6 @@ public interface Session
interface Command
extends Channel {
/**
* Read from the command's {@code stderr} stream into a string (blocking).
*
* @return the commands {@code stderr} output as a string
*
* @throws IOException if error reading from the stream
*/
String getErrorAsString()
throws IOException;
/** Returns the command's {@code stderr} stream. */
InputStream getErrorStream();
@@ -81,16 +71,6 @@ public interface Session
*/
Boolean getExitWasCoreDumped();
/**
* Read from the command's {@code stdout} stream into a string (blocking).
*
* @return the command's {@code stdout} output as a string
*
* @throws IOException if error reading from the stream
*/
String getOutputAsString()
throws IOException;
/**
* Send a signal to the remote command.
*
@@ -101,6 +81,14 @@ public interface Session
void signal(Signal signal)
throws TransportException;
@Deprecated
String getOutputAsString()
throws IOException;
@Deprecated
String getErrorAsString()
throws IOException;
}
/** Shell API. */
@@ -146,6 +134,7 @@ public interface Session
/** Subsystem API. */
interface Subsystem
extends Channel {
Integer getExitStatus();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,9 +36,11 @@
package net.schmizz.sshj.connection.channel.direct;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.common.SSHRuntimeException;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.ChannelInputStream;
@@ -51,8 +53,7 @@ import java.util.Map;
import java.util.concurrent.TimeUnit;
/** {@link Session} implementation. */
public class
SessionChannel
public class SessionChannel
extends AbstractDirectChannel
implements Session, Session.Command, Session.Shell, Session.Subsystem {
@@ -66,6 +67,8 @@ public class
private Boolean canDoFlowControl;
private boolean usedUp;
public SessionChannel(Connection conn) {
super(conn, "session");
}
@@ -73,7 +76,7 @@ public class
@Override
public void allocateDefaultPTY()
throws ConnectionException, TransportException {
allocatePTY("vt100", 80, 24, 0, 0, Collections.<PTYMode, Integer>emptyMap());
allocatePTY("dummy", 80, 24, 0, 0, Collections.<PTYMode, Integer>emptyMap());
}
@Override
@@ -84,10 +87,10 @@ public class
true,
new Buffer.PlainBuffer()
.putString(term)
.putInt(cols)
.putInt(rows)
.putInt(width)
.putInt(height)
.putUInt32(cols)
.putUInt32(rows)
.putUInt32(width)
.putUInt32(height)
.putBytes(PTYMode.encode(modes))
).await(conn.getTimeout(), TimeUnit.SECONDS);
}
@@ -101,31 +104,27 @@ public class
public void changeWindowDimensions(int cols, int rows, int width, int height)
throws TransportException {
sendChannelRequest(
"pty-req",
"window-change",
false,
new Buffer.PlainBuffer()
.putInt(cols)
.putInt(rows)
.putInt(width)
.putInt(height)
.putUInt32(cols)
.putUInt32(rows)
.putUInt32(width)
.putUInt32(height)
);
}
@Override
public Command exec(String command)
throws ConnectionException, TransportException {
checkReuse();
log.info("Will request to exec `{}`", command);
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command))
.await(conn.getTimeout(), TimeUnit.SECONDS);
usedUp = true;
return this;
}
@Override
public String getErrorAsString()
throws IOException {
return StreamCopier.copyStreamToString(err);
}
@Override
public InputStream getErrorStream() {
return err;
@@ -146,32 +145,29 @@ public class
return exitStatus;
}
@Override
public String getOutputAsString()
throws IOException {
return StreamCopier.copyStreamToString(getInputStream());
}
@Override
public void handleRequest(String req, SSHPacket buf)
throws ConnectionException, TransportException {
if ("xon-xoff".equals(req))
canDoFlowControl = buf.readBoolean();
else if ("exit-status".equals(req))
exitStatus = buf.readInt();
else if ("exit-signal".equals(req)) {
exitSignal = Signal.fromString(buf.readString());
wasCoreDumped = buf.readBoolean(); // core dumped
exitErrMsg = buf.readString();
sendClose();
} else
super.handleRequest(req, buf);
try {
if ("xon-xoff".equals(req))
canDoFlowControl = buf.readBoolean();
else if ("exit-status".equals(req))
exitStatus = buf.readUInt32AsInt();
else if ("exit-signal".equals(req)) {
exitSignal = Signal.fromString(buf.readString());
wasCoreDumped = buf.readBoolean(); // core dumped
exitErrMsg = buf.readString();
sendClose();
} else
super.handleRequest(req, buf);
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
}
@Override
public void reqX11Forwarding(String authProto, String authCookie, int screen)
throws ConnectionException,
TransportException {
throws ConnectionException, TransportException {
sendChannelRequest(
"x11-req",
true,
@@ -179,7 +175,7 @@ public class
.putBoolean(false)
.putString(authProto)
.putString(authCookie)
.putInt(screen)
.putUInt32(screen)
).await(conn.getTimeout(), TimeUnit.SECONDS);
}
@@ -199,16 +195,20 @@ public class
@Override
public Shell startShell()
throws ConnectionException, TransportException {
checkReuse();
sendChannelRequest("shell", true, null).await(conn.getTimeout(), TimeUnit.SECONDS);
usedUp = true;
return this;
}
@Override
public Subsystem startSubsystem(String name)
throws ConnectionException, TransportException {
checkReuse();
log.info("Will request `{}` subsystem", name);
sendChannelRequest("subsystem", true, new Buffer.PlainBuffer().putString(name))
.await(conn.getTimeout(), TimeUnit.SECONDS);
usedUp = true;
return this;
}
@@ -230,12 +230,43 @@ public class
}
@Override
protected void gotExtendedData(int dataTypeCode, SSHPacket buf)
protected void gotExtendedData(SSHPacket buf)
throws ConnectionException, TransportException {
if (dataTypeCode == 1)
receiveInto(err, buf);
else
super.gotExtendedData(dataTypeCode, buf);
try {
final int dataTypeCode = buf.readUInt32AsInt();
if (dataTypeCode == 1)
receiveInto(err, buf);
else
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
"Bad extended data type = " + dataTypeCode);
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
}
}
@Override
public void notifyError(SSHException error) {
err.notifyError(error);
super.notifyError(error);
}
private void checkReuse() {
if (usedUp)
throw new SSHRuntimeException("This session channel is all used up");
}
@Override
@Deprecated
public String getOutputAsString()
throws IOException {
return IOUtils.readFully(getInputStream()).toString();
}
@Override
@Deprecated
public String getErrorAsString()
throws IOException {
return IOUtils.readFully(getErrorStream()).toString();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,21 +19,20 @@ 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. */
public enum Signal {
ABRT("ABRT"),
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");
ABRT,
ALRM,
FPE,
HUP,
ILL,
INT,
KILL,
PIPE,
QUIT,
SEGV,
TERM,
USR1,
USR2,
UNKNOWN;
/**
* Create from the string representation used when the signal is received as part of an SSH packet.
@@ -44,20 +43,9 @@ public enum Signal {
*/
public static Signal fromString(String name) {
for (Signal sig : Signal.values())
if (sig.name.equals(name))
if (sig.toString().equals(name))
return sig;
return UNKNOWN;
}
private final String name;
private Signal(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -69,9 +69,9 @@ public abstract class AbstractForwardedChannel
// Must ensure channel is attached before confirming, data could start coming in immediately!
conn.attach(this);
trans.write(newBuffer(Message.CHANNEL_OPEN_CONFIRMATION)
.putInt(getID())
.putInt(getLocalWinSize())
.putInt(getLocalMaxPacketSize()));
.putUInt32(getID())
.putUInt32(getLocalWinSize())
.putUInt32(getLocalMaxPacketSize()));
open.set();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,7 +50,7 @@ public abstract class AbstractForwardedChannelOpener
new Thread() {
{
setName("ConnectListener");
setName("chanopener");
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,18 +39,18 @@ public class RemotePortForwarder
* address (or domain name) and port on which connections for forwarding
* are to be accepted. Some strings used for 'address to bind' have
* special-case semantics.
* <p/>
*
* o &quot;&quot; means that connections are to be accepted on all protocol
* families supported by the SSH implementation.
* <p/>
*
* o &quot;0.0.0.0&quot; means to listen on all IPv4 addresses.
* <p/>
*
* o &quot;::&quot; means to listen on all IPv6 addresses.
* <p/>
*
* o &quot;localhost&quot; means to listen on all protocol families supported by
* the SSH implementation on loopback addresses only ([RFC3330] and
* [RFC3513]).
* <p/>
*
* o &quot;127.0.0.1&quot; and &quot;::1&quot; indicate listening on the loopback
* interfaces for IPv4 and IPv6, respectively.
* </pre>
@@ -168,7 +168,11 @@ public class RemotePortForwarder
throws ConnectionException, TransportException {
SSHPacket reply = req(PF_REQ, forward);
if (forward.port == 0)
forward.port = reply.readInt();
try {
forward.port = reply.readUInt32AsInt();
} catch (Buffer.BufferException e) {
throw new ConnectionException(e);
}
log.info("Remote end listening on {}", forward);
listeners.put(forward, listener);
return forward;
@@ -193,10 +197,10 @@ public class RemotePortForwarder
protected SSHPacket req(String reqName, Forward forward)
throws ConnectionException, TransportException {
final byte[] specifics = new Buffer.PlainBuffer().putString(forward.address).putInt(forward.port)
.getCompactData();
final byte[] specifics = new Buffer.PlainBuffer().putString(forward.address).putUInt32(forward.port)
.getCompactData();
return conn.sendGlobalRequest(reqName, true, specifics)
.get(conn.getTimeout(), TimeUnit.SECONDS);
.retrieve(conn.getTimeout(), TimeUnit.SECONDS);
}
/** @return the active forwards. */
@@ -211,14 +215,19 @@ public class RemotePortForwarder
@Override
public void handleOpen(SSHPacket buf)
throws ConnectionException, TransportException {
final ForwardedTCPIPChannel chan = new ForwardedTCPIPChannel(conn, buf.readInt(), buf.readInt(), buf.readInt(),
new Forward(buf.readString(), buf.readInt()),
buf.readString(), buf.readInt());
final ForwardedTCPIPChannel chan;
try {
chan = new ForwardedTCPIPChannel(conn, buf.readUInt32AsInt(), buf.readUInt32AsInt(), buf.readUInt32AsInt(),
new Forward(buf.readString(), buf.readUInt32AsInt()),
buf.readString(), buf.readUInt32AsInt());
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
if (listeners.containsKey(chan.getParentForward()))
callListener(listeners.get(chan.getParentForward()), chan);
else
chan.reject(OpenFailException.Reason.ADMINISTRATIVELY_PROHIBITED, "Forwarding was not requested on `"
+ chan.getParentForward() + "`");
+ chan.getParentForward() + "`");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,16 +15,17 @@
*/
package net.schmizz.sshj.connection.channel.forwarded;
import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.common.StreamCopier.ErrorCallback;
import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;
/** A {@link ConnectListener} that forwards what is received over the channel to a socket and vice-versa. */
public class SocketForwardingConnectListener
@@ -54,25 +55,15 @@ public class SocketForwardingConnectListener
// ok so far -- could connect, let's confirm the channel
chan.confirm();
final ErrorCallback closer = StreamCopier.closeOnErrorCallback(chan, new Closeable() {
@Override
public void close()
throws IOException {
sock.close();
}
});
new StreamCopier("soc2chan", sock.getInputStream(), chan.getOutputStream())
final Event<IOException> soc2chan = new StreamCopier(sock.getInputStream(), chan.getOutputStream())
.bufSize(chan.getRemoteMaxPacketSize())
.errorCallback(closer)
.daemon(true)
.start();
.spawnDaemon("soc2chan");
new StreamCopier("chan2soc", chan.getInputStream(), sock.getOutputStream())
final Event<IOException> chan2soc = new StreamCopier(chan.getInputStream(), sock.getOutputStream())
.bufSize(chan.getLocalMaxPacketSize())
.errorCallback(closer)
.daemon(true)
.start();
.spawnDaemon("chan2soc");
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, chan2soc, soc2chan, chan, sock);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.connection.channel.forwarded;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.connection.ConnectionException;
@@ -55,10 +56,14 @@ public class X11Forwarder
@Override
public void handleOpen(SSHPacket buf)
throws ConnectionException, TransportException {
callListener(listener, new X11Channel(conn,
buf.readInt(),
buf.readInt(), buf.readInt(),
buf.readString(), buf.readInt()));
try {
callListener(listener, new X11Channel(conn,
buf.readUInt32AsInt(),
buf.readUInt32AsInt(), buf.readUInt32AsInt(),
buf.readString(), buf.readUInt32AsInt()));
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
}
/** Stop handling {@code x11} channel open requests. De-registers itself with connection layer. */

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -119,26 +119,26 @@ public final class FileAttributes {
public byte[] toBytes() {
Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
buf.putInt(mask);
buf.putUInt32(mask);
if (has(Flag.SIZE))
buf.putUINT64(size);
buf.putUInt64(size);
if (has(Flag.UIDGID)) {
buf.putInt(uid);
buf.putInt(gid);
buf.putUInt32(uid);
buf.putUInt32(gid);
}
if (has(Flag.MODE))
buf.putInt(mode.getMask());
buf.putUInt32(mode.getMask());
if (has(Flag.ACMODTIME)) {
buf.putInt(atime);
buf.putInt(mtime);
buf.putUInt32(atime);
buf.putUInt32(mtime);
}
if (has(Flag.EXTENDED)) {
buf.putInt(ext.size());
buf.putUInt32(ext.size());
for (Entry<String, String> entry : ext.entrySet()) {
buf.putString(entry.getKey());
buf.putString(entry.getValue());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -74,7 +74,7 @@ public class FileMode {
}
public int getTypeMask() {
return mask & 0770000;
return mask & 0170000;
}
public int getPermissionsMask() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
*/
package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Future;
import net.schmizz.concurrent.Promise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,12 +31,14 @@ public class PacketReader
private final Logger log = LoggerFactory.getLogger(getClass());
private final InputStream in;
private final Map<Long, Future<Response, SFTPException>> futures = new ConcurrentHashMap<Long, Future<Response, SFTPException>>();
private final Map<Long, Promise<Response, SFTPException>> promises = new ConcurrentHashMap<Long, Promise<Response, SFTPException>>();
private final SFTPPacket<Response> packet = new SFTPPacket<Response>();
private final byte[] lenBuf = new byte[4];
private final SFTPEngine engine;
public PacketReader(InputStream in) {
this.in = in;
public PacketReader(SFTPEngine engine) {
this.engine = engine;
this.in = engine.getSubsystem().getInputStream();
setName("sftp reader");
}
@@ -55,9 +57,9 @@ public class PacketReader
readIntoBuffer(lenBuf, 0, lenBuf.length);
return (int) (lenBuf[0] << 24 & 0xff000000L
| lenBuf[1] << 16 & 0x00ff0000L
| lenBuf[2] << 8 & 0x0000ff00L
| lenBuf[3] & 0x000000ffL);
| lenBuf[1] << 16 & 0x00ff0000L
| lenBuf[2] << 8 & 0x0000ff00L
| lenBuf[3] & 0x000000ffL);
}
public SFTPPacket<Response> readPacket()
@@ -78,30 +80,30 @@ public class PacketReader
@Override
public void run() {
try {
while (true) {
while (!isInterrupted()) {
readPacket();
handle();
}
} catch (IOException e) {
for (Future<Response, SFTPException> future : futures.values())
future.error(e);
for (Promise<Response, SFTPException> promise : promises.values())
promise.deliverError(e);
}
}
public void handle()
throws SFTPException {
Response resp = new Response(packet);
Future<Response, SFTPException> future = futures.remove(resp.getRequestID());
Response resp = new Response(packet, engine.getOperativeProtocolVersion());
Promise<Response, SFTPException> promise = promises.remove(resp.getRequestID());
log.debug("Received {} packet", resp.getType());
if (future == null)
if (promise == null)
throw new SFTPException("Received [" + resp.readType() + "] response for request-id " + resp.getRequestID()
+ ", no such request was made");
+ ", no such request was made");
else
future.set(resp);
promise.deliver(resp);
}
public void expectResponseTo(Request req) {
futures.put(req.getRequestID(), req.getResponseFuture());
promises.put(req.getRequestID(), req.getResponsePromise());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,8 +52,7 @@ public enum PacketType {
static {
for (PacketType t : PacketType.values())
if (cache[t.toByte() & 0xff] == null)
cache[t.toByte() & 0xff] = t;
cache[t.toByte() & 0xff] = t;
}
private PacketType(int b) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,23 +17,23 @@ package net.schmizz.sshj.sftp;
public class PathComponents {
public static String adjustForParent(String parent, String path) {
return (path.startsWith("/")) ? path // Absolute path, nothing to adjust
: (parent + (parent.endsWith("/") ? "" : "/") + path); // Relative path
static String adjustForParent(String parent, String path, String pathSep) {
return (path.startsWith(pathSep)) ? path // Absolute path, nothing to adjust
: (parent + (parent.endsWith(pathSep) ? "" : pathSep) + path); // Relative path
}
private static String trimFinalSlash(String path) {
return path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
static String trimTrailingSeparator(String somePath, String pathSep) {
return somePath.endsWith(pathSep) ? somePath.substring(0, somePath.length() - pathSep.length()) : somePath;
}
private final String parent;
private final String name;
private final String path;
public PathComponents(String parent, String name) {
public PathComponents(String parent, String name, String pathSep) {
this.parent = parent;
this.name = name;
this.path = adjustForParent(parent, name);
this.path = trimTrailingSeparator(adjustForParent(parent, name, pathSep), pathSep);
}
public String getParent() {
@@ -50,17 +50,12 @@ public class PathComponents {
@Override
public boolean equals(Object o) {
if (o instanceof PathComponents) {
final PathComponents that = (PathComponents) o;
return (trimFinalSlash(path).equals(trimFinalSlash(that.path)));
}
return false;
return this == o || ((o instanceof PathComponents) && path.equals(((PathComponents) o).path));
}
@Override
public int hashCode() {
return trimFinalSlash(path).hashCode();
return path.hashCode();
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,11 +19,32 @@ import java.io.IOException;
public class PathHelper {
private final SFTPEngine sftp;
public static final String DEFAULT_PATH_SEPARATOR = "/";
private final SFTPEngine engine;
private final String pathSep;
private String dotDir;
public PathHelper(SFTPEngine sftp) {
this.sftp = sftp;
public PathHelper(SFTPEngine engine, String pathSep) {
this.engine = engine;
this.pathSep = pathSep;
}
public String adjustForParent(String parent, String path) {
return PathComponents.adjustForParent(parent, path, pathSep);
}
public String trimTrailingSeparator(String path) {
return PathComponents.trimTrailingSeparator(path, pathSep);
}
public String getPathSeparator() {
return pathSep;
}
public PathComponents getComponents(String parent, String name) {
return new PathComponents(parent, name, pathSep);
}
public PathComponents getComponents(String path)
@@ -31,21 +52,21 @@ public class PathHelper {
if (path.isEmpty() || path.equals("."))
return getComponents(getDotDir());
final int lastSlash = path.lastIndexOf("/");
final int lastSlash = path.lastIndexOf(pathSep);
if (lastSlash == -1)
if (lastSlash == -1) // Relative path
if (path.equals(".."))
return getComponents(canon(path));
else
return new PathComponents(getDotDir(), path);
return getComponents(getDotDir(), path);
final String name = path.substring(lastSlash + 1);
final String name = path.substring(lastSlash + pathSep.length());
if (name.equals(".") || name.equals(".."))
return getComponents(canon(path));
else {
final String parent = path.substring(0, lastSlash);
return new PathComponents(parent, name);
return getComponents(parent, name);
}
}
@@ -56,7 +77,7 @@ public class PathHelper {
private String canon(String path)
throws IOException {
return sftp.canonicalize(path);
return engine.canonicalize(path);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,17 +32,18 @@ public class RemoteDirectory
throws IOException {
List<RemoteResourceInfo> rri = new LinkedList<RemoteResourceInfo>();
loop:
for (; ;) {
for (; ; ) {
Response res = requester.doRequest(newRequest(PacketType.READDIR));
switch (res.getType()) {
case NAME:
final int count = res.readInt();
final int count = res.readUInt32AsInt();
for (int i = 0; i < count; i++) {
final String name = res.readString();
res.readString(); // long name - IGNORED - shdve never been in the protocol
final FileAttributes attrs = res.readFileAttributes();
RemoteResourceInfo inf = new RemoteResourceInfo(path, name, attrs);
final PathComponents comps = requester.getPathHelper().getComponents(path, name);
final RemoteResourceInfo inf = new RemoteResourceInfo(comps, attrs);
if (!(name.equals(".") || name.equals("..")) && (filter == null || filter.accept(inf)))
rri.add(inf);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,8 +39,8 @@ public class RemoteFile
public FileAttributes fetchAttributes()
throws IOException {
return requester.doRequest(newRequest(PacketType.FSTAT))
.ensurePacketTypeIs(PacketType.ATTRS)
.readFileAttributes();
.ensurePacketTypeIs(PacketType.ATTRS)
.readFileAttributes();
}
public long length()
@@ -55,10 +55,10 @@ public class RemoteFile
public int read(long fileOffset, byte[] to, int offset, int len)
throws IOException {
Response res = requester.doRequest(newRequest(PacketType.READ).putUINT64(fileOffset).putInt(len));
Response res = requester.doRequest(newRequest(PacketType.READ).putUInt64(fileOffset).putUInt32(len));
switch (res.getType()) {
case DATA:
int recvLen = res.readInt();
int recvLen = res.readUInt32AsInt();
System.arraycopy(res.array(), res.rpos(), to, offset, recvLen);
return recvLen;
@@ -74,9 +74,9 @@ public class RemoteFile
public void write(long fileOffset, byte[] data, int off, int len)
throws IOException {
requester.doRequest(newRequest(PacketType.WRITE)
.putUINT64(fileOffset)
.putInt(len - off)
.putRawBytes(data, off, len)
.putUInt64(fileOffset)
.putUInt32(len - off)
.putRawBytes(data, off, len)
).ensureStatusPacketIsOK();
}
@@ -87,12 +87,12 @@ public class RemoteFile
public int getOutgoingPacketOverhead() {
return 1 + // packet type
4 + // request id
4 + // next length
handle.length() + // next
8 + // file offset
4 + // data length
4; // packet length
4 + // request id
4 + // next length
handle.length() + // next
8 + // file offset
4 + // data length
4; // packet length
}
public class RemoteFileOutputStream

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
abstract class RemoteResource
public abstract class RemoteResource
implements Closeable {
/** Logger */

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,10 +20,6 @@ public class RemoteResourceInfo {
private final PathComponents comps;
private final FileAttributes attrs;
public RemoteResourceInfo(String parent, String name, FileAttributes attrs) {
this(new PathComponents(parent, name), attrs);
}
public RemoteResourceInfo(PathComponents comps, FileAttributes attrs) {
this.comps = comps;
this.attrs = attrs;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,21 +15,21 @@
*/
package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Future;
import net.schmizz.concurrent.Promise;
public class Request
extends SFTPPacket<Request> {
private final PacketType type;
private final long reqID;
private final Future<Response, SFTPException> responseFuture;
private final Promise<Response, SFTPException> responsePromise;
public Request(PacketType type, long reqID) {
super(type);
this.type = type;
this.reqID = reqID;
responseFuture = new Future<Response, SFTPException>("sftp / " + reqID, SFTPException.chainer);
putInt(reqID);
responsePromise = new Promise<Response, SFTPException>("sftp / " + reqID, SFTPException.chainer);
putUInt32(reqID);
}
public long getRequestID() {
@@ -40,8 +40,8 @@ public class Request
return type;
}
public Future<Response, SFTPException> getResponseFuture() {
return responseFuture;
public Promise<Response, SFTPException> getResponsePromise() {
return responsePromise;
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ import java.io.IOException;
public interface Requester {
PathHelper getPathHelper();
Request newRequest(PacketType type);
Response doRequest(Request req)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,13 +47,24 @@ public class Response
}
private final int protocolVersion;
private final PacketType type;
private final long reqID;
public Response(Buffer<Response> pk) {
public Response(Buffer<Response> pk, int protocolVersion)
throws SFTPException {
super(pk);
this.protocolVersion = protocolVersion;
this.type = readType();
this.reqID = readLong();
try {
this.reqID = readUInt32();
} catch (BufferException be) {
throw new SFTPException(be);
}
}
public int getProtocolVersion() {
return protocolVersion;
}
public long getRequestID() {
@@ -64,15 +75,20 @@ public class Response
return type;
}
public StatusCode readStatusCode() {
return StatusCode.fromInt(readInt());
public StatusCode readStatusCode()
throws SFTPException {
try {
return StatusCode.fromInt(readUInt32AsInt());
} catch (BufferException be) {
throw new SFTPException(be);
}
}
public Response ensurePacketTypeIs(PacketType pt)
throws SFTPException {
if (getType() != pt)
if (getType() == PacketType.STATUS)
throw new SFTPException(readStatusCode(), readString());
error(readStatusCode());
else
throw new SFTPException("Unexpected packet " + getType());
return this;
@@ -85,10 +101,19 @@ public class Response
public Response ensureStatusIs(StatusCode acceptable)
throws SFTPException {
StatusCode sc = readStatusCode();
final StatusCode sc = readStatusCode();
if (sc != acceptable)
throw new SFTPException(sc, readString());
error(sc);
return this;
}
protected String error(StatusCode sc)
throws SFTPException {
try {
throw new SFTPException(sc, protocolVersion < 3 ? sc.toString() : readString());
} catch (BufferException be) {
throw new SFTPException(be);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,13 @@
*/
package net.schmizz.sshj.sftp;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import net.schmizz.sshj.xfer.FilePermission;
import net.schmizz.sshj.xfer.LocalDestFile;
import net.schmizz.sshj.xfer.LocalSourceFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.util.Deque;
import java.util.EnumSet;
@@ -27,24 +29,22 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class SFTPClient {
public class SFTPClient
implements Closeable {
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
private final SFTPEngine sftp;
private final SFTPFileTransfer xfer;
private final PathHelper pathHelper;
protected final SFTPEngine engine;
protected final SFTPFileTransfer xfer;
public SFTPClient(SessionFactory ssh)
throws IOException {
this.sftp = new SFTPEngine(ssh).init();
this.pathHelper = new PathHelper(sftp);
this.xfer = new SFTPFileTransfer(sftp);
public SFTPClient(SFTPEngine engine) {
this.engine = engine;
this.xfer = new SFTPFileTransfer(engine);
}
public SFTPEngine getSFTPEngine() {
return sftp;
return engine;
}
public SFTPFileTransfer getFileTansfer() {
@@ -58,7 +58,7 @@ public class SFTPClient {
public List<RemoteResourceInfo> ls(String path, RemoteResourceFilter filter)
throws IOException {
final RemoteDirectory dir = sftp.openDir(path);
final RemoteDirectory dir = engine.openDir(path);
try {
return dir.scan(filter);
} finally {
@@ -69,7 +69,7 @@ public class SFTPClient {
public RemoteFile open(String filename, Set<OpenMode> mode, FileAttributes attrs)
throws IOException {
log.debug("Opening `{}`", filename);
return sftp.open(filename, mode, attrs);
return engine.open(filename, mode, attrs);
}
public RemoteFile open(String filename, Set<OpenMode> mode)
@@ -84,14 +84,14 @@ public class SFTPClient {
public void mkdir(String dirname)
throws IOException {
sftp.makeDir(dirname);
engine.makeDir(dirname);
}
public void mkdirs(String path)
throws IOException {
final Deque<String> dirsToMake = new LinkedList<String>();
for (PathComponents current = pathHelper.getComponents(path); ; current = pathHelper
.getComponents(current.getParent())) {
for (PathComponents current = engine.getPathHelper().getComponents(path); ;
current = engine.getPathHelper().getComponents(current.getParent())) {
final FileAttributes attrs = statExistence(current.getPath());
if (attrs == null) {
dirsToMake.push(current.getPath());
@@ -109,7 +109,7 @@ public class SFTPClient {
public FileAttributes statExistence(String path)
throws IOException {
try {
return sftp.stat(path);
return engine.stat(path);
} catch (SFTPException sftpe) {
if (sftpe.getStatusCode() == Response.StatusCode.NO_SUCH_FILE) {
return null;
@@ -121,31 +121,31 @@ public class SFTPClient {
public void rename(String oldpath, String newpath)
throws IOException {
sftp.rename(oldpath, newpath);
engine.rename(oldpath, newpath);
}
public void rm(String filename)
throws IOException {
sftp.remove(filename);
engine.remove(filename);
}
public void rmdir(String dirname)
throws IOException {
sftp.removeDir(dirname);
engine.removeDir(dirname);
}
public void symlink(String linkpath, String targetpath)
throws IOException {
sftp.symlink(linkpath, targetpath);
engine.symlink(linkpath, targetpath);
}
public int version() {
return sftp.getOperativeProtocolVersion();
return engine.getOperativeProtocolVersion();
}
public void setattr(String path, FileAttributes attrs)
throws IOException {
sftp.setAttributes(path, attrs);
engine.setAttributes(path, attrs);
}
public int uid(String path)
@@ -185,17 +185,17 @@ public class SFTPClient {
public String readlink(String path)
throws IOException {
return sftp.readLink(path);
return engine.readLink(path);
}
public FileAttributes stat(String path)
throws IOException {
return sftp.stat(path);
return engine.stat(path);
}
public FileAttributes lstat(String path)
throws IOException {
return sftp.lstat(path);
return engine.lstat(path);
}
public void chown(String path, int uid)
@@ -220,7 +220,7 @@ public class SFTPClient {
public String canonicalize(String path)
throws IOException {
return sftp.canonicalize(path);
return engine.canonicalize(path);
}
public long size(String path)
@@ -238,4 +238,20 @@ public class SFTPClient {
xfer.upload(source, dest);
}
public void get(String source, LocalDestFile dest)
throws IOException {
xfer.download(source, dest);
}
public void put(LocalSourceFile source, String dest)
throws IOException {
xfer.upload(source, dest);
}
@Override
public void close()
throws IOException {
engine.close();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.util.EnumSet;
@@ -30,39 +31,42 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
public class SFTPEngine
implements Requester {
/** Logger */
private final Logger log = LoggerFactory.getLogger(getClass());
public static final int PROTOCOL_VERSION = 3;
implements Requester, Closeable {
public static final int MAX_SUPPORTED_VERSION = 3;
public static final int DEFAULT_TIMEOUT = 30;
private volatile int timeout = DEFAULT_TIMEOUT;
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
private final Subsystem sub;
private final PacketReader reader;
private final OutputStream out;
protected volatile int timeout = DEFAULT_TIMEOUT;
private long reqID;
private int negotiatedVersion;
private final Map<String, String> serverExtensions = new HashMap<String, String>();
protected final PathHelper pathHelper;
protected final Subsystem sub;
protected final PacketReader reader;
protected final OutputStream out;
protected long reqID;
protected int operativeVersion;
protected final Map<String, String> serverExtensions = new HashMap<String, String>();
public SFTPEngine(SessionFactory ssh)
throws SSHException {
sub = ssh.startSession().startSubsystem("sftp");
out = sub.getOutputStream();
reader = new PacketReader(sub.getInputStream());
this(ssh, PathHelper.DEFAULT_PATH_SEPARATOR);
}
public Subsystem getSubsystem() {
return sub;
public SFTPEngine(SessionFactory ssh, String pathSep)
throws SSHException {
sub = ssh.startSession().startSubsystem("sftp");
out = sub.getOutputStream();
reader = new PacketReader(this);
pathHelper = new PathHelper(this, pathSep);
}
public SFTPEngine init()
throws IOException {
transmit(new SFTPPacket<Request>(PacketType.INIT).putInt(PROTOCOL_VERSION));
transmit(new SFTPPacket<Request>(PacketType.INIT).putUInt32(MAX_SUPPORTED_VERSION));
final SFTPPacket<Response> response = reader.readPacket();
@@ -70,10 +74,10 @@ public class SFTPEngine
if (type != PacketType.VERSION)
throw new SFTPException("Expected INIT packet, received: " + type);
negotiatedVersion = response.readInt();
log.info("Client version {}, server version {}", PROTOCOL_VERSION, negotiatedVersion);
if (negotiatedVersion < PROTOCOL_VERSION)
throw new SFTPException("Server reported protocol version: " + negotiatedVersion);
operativeVersion = response.readUInt32AsInt();
log.info("Server version {}", operativeVersion);
if (MAX_SUPPORTED_VERSION < operativeVersion)
throw new SFTPException("Server reported incompatible protocol version: " + operativeVersion);
while (response.available() > 0)
serverExtensions.put(response.readString(), response.readString());
@@ -83,8 +87,21 @@ public class SFTPEngine
return this;
}
public Subsystem getSubsystem() {
return sub;
}
public int getOperativeProtocolVersion() {
return negotiatedVersion;
return operativeVersion;
}
public Request newExtendedRequest(String reqName) {
return newRequest(PacketType.EXTENDED).putString(reqName);
}
@Override
public PathHelper getPathHelper() {
return pathHelper;
}
@Override
@@ -98,24 +115,13 @@ public class SFTPEngine
reader.expectResponseTo(req);
log.debug("Sending {}", req);
transmit(req);
return req.getResponseFuture().get(timeout, TimeUnit.SECONDS);
}
private synchronized void transmit(SFTPPacket<Request> payload)
throws IOException {
final int len = payload.available();
out.write((len >>> 24) & 0xff);
out.write((len >>> 16) & 0xff);
out.write((len >>> 8) & 0xff);
out.write(len & 0xff);
out.write(payload.array(), payload.rpos(), len);
out.flush();
return req.getResponsePromise().retrieve(timeout, TimeUnit.SECONDS);
}
public RemoteFile open(String path, Set<OpenMode> modes, FileAttributes fa)
throws IOException {
final String handle = doRequest(
newRequest(PacketType.OPEN).putString(path).putInt(OpenMode.toMask(modes)).putFileAttributes(fa)
newRequest(PacketType.OPEN).putString(path).putUInt32(OpenMode.toMask(modes)).putFileAttributes(fa)
).ensurePacketTypeIs(PacketType.HANDLE).readString();
return new RemoteFile(this, path, handle);
}
@@ -147,6 +153,8 @@ public class SFTPEngine
public String readLink(String path)
throws IOException {
if (operativeVersion < 3)
throw new SFTPException("READLINK is not supported in SFTPv" + operativeVersion);
return readSingleName(
doRequest(
newRequest(PacketType.READLINK).putString(path)
@@ -165,6 +173,8 @@ public class SFTPEngine
public void symlink(String linkpath, String targetpath)
throws IOException {
if (operativeVersion < 3)
throw new SFTPException("SYMLINK is not supported in SFTPv" + operativeVersion);
doRequest(
newRequest(PacketType.SYMLINK).putString(linkpath).putString(targetpath)
).ensureStatusPacketIsOK();
@@ -184,13 +194,6 @@ public class SFTPEngine
).ensureStatusIs(Response.StatusCode.OK);
}
private FileAttributes stat(PacketType pt, String path)
throws IOException {
return doRequest(newRequest(pt).putString(path))
.ensurePacketTypeIs(PacketType.ATTRS)
.readFileAttributes();
}
public FileAttributes stat(String path)
throws IOException {
return stat(PacketType.STAT, path);
@@ -203,6 +206,8 @@ public class SFTPEngine
public void rename(String oldPath, String newPath)
throws IOException {
if (operativeVersion < 1)
throw new SFTPException("RENAME is not supported in SFTPv" + operativeVersion);
doRequest(
newRequest(PacketType.RENAME).putString(oldPath).putString(newPath)
).ensureStatusPacketIsOK();
@@ -216,15 +221,6 @@ public class SFTPEngine
));
}
private static String readSingleName(Response res)
throws IOException {
res.ensurePacketTypeIs(PacketType.NAME);
if (res.readInt() == 1)
return res.readString();
else
throw new SFTPException("Unexpected data in " + res.getType() + " packet");
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
@@ -233,4 +229,37 @@ public class SFTPEngine
return timeout;
}
@Override
public void close()
throws IOException {
sub.close();
}
protected FileAttributes stat(PacketType pt, String path)
throws IOException {
return doRequest(newRequest(pt).putString(path))
.ensurePacketTypeIs(PacketType.ATTRS)
.readFileAttributes();
}
protected static String readSingleName(Response res)
throws IOException {
res.ensurePacketTypeIs(PacketType.NAME);
if (res.readUInt32AsInt() == 1)
return res.readString();
else
throw new SFTPException("Unexpected data in " + res.getType() + " packet");
}
protected synchronized void transmit(SFTPPacket<Request> payload)
throws IOException {
final int len = payload.available();
out.write((len >>> 24) & 0xff);
out.write((len >>> 16) & 0xff);
out.write((len >>> 8) & 0xff);
out.write(len & 0xff);
out.write(payload.array(), payload.rpos(), len);
out.flush();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -67,7 +67,6 @@ public class SFTPException
public StatusCode getStatusCode() {
return (sc == null) ? StatusCode.UNKNOWN : sc;
}
public SFTPException(StatusCode sc, String msg) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,69 +18,66 @@ package net.schmizz.sshj.sftp;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.sftp.Response.StatusCode;
import net.schmizz.sshj.xfer.AbstractFileTransfer;
import net.schmizz.sshj.xfer.FileSystemFile;
import net.schmizz.sshj.xfer.FileTransfer;
import net.schmizz.sshj.xfer.FileTransferUtil;
import net.schmizz.sshj.xfer.LocalDestFile;
import net.schmizz.sshj.xfer.LocalFileFilter;
import net.schmizz.sshj.xfer.LocalSourceFile;
import net.schmizz.sshj.xfer.TransferListener;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.EnumSet;
public class SFTPFileTransfer
extends AbstractFileTransfer
implements FileTransfer {
private final SFTPEngine sftp;
private final PathHelper pathHelper;
private final SFTPEngine engine;
private volatile FileFilter uploadFilter = defaultLocalFilter;
private volatile RemoteResourceFilter downloadFilter = defaultRemoteFilter;
private volatile LocalFileFilter uploadFilter;
private volatile RemoteResourceFilter downloadFilter;
private static final FileFilter defaultLocalFilter = new FileFilter() {
@Override
public boolean accept(File pathName) {
return true;
}
};
private static final RemoteResourceFilter defaultRemoteFilter = new RemoteResourceFilter() {
@Override
public boolean accept(RemoteResourceInfo resource) {
return true;
}
};
public SFTPFileTransfer(SFTPEngine sftp) {
this.sftp = sftp;
this.pathHelper = new PathHelper(sftp);
public SFTPFileTransfer(SFTPEngine engine) {
this.engine = engine;
}
@Override
public void upload(String source, String dest)
throws IOException {
new Uploader().upload(new File(source), dest);
new Uploader().upload(new FileSystemFile(source), dest);
}
@Override
public void download(String source, String dest)
throws IOException {
final PathComponents pathComponents = pathHelper.getComponents(source);
final FileAttributes attributes = sftp.stat(source);
new Downloader().download(new RemoteResourceInfo(pathComponents, attributes), new File(dest));
download(source, new FileSystemFile(dest));
}
public void setUploadFilter(FileFilter uploadFilter) {
this.uploadFilter = (this.uploadFilter == null) ? defaultLocalFilter : uploadFilter;
@Override
public void upload(LocalSourceFile localFile, String remotePath)
throws IOException {
new Uploader().upload(localFile, remotePath);
}
@Override
public void download(String source, LocalDestFile dest)
throws IOException {
final PathComponents pathComponents = engine.getPathHelper().getComponents(source);
final FileAttributes attributes = engine.stat(source);
new Downloader().download(new RemoteResourceInfo(pathComponents, attributes), dest);
}
public void setUploadFilter(LocalFileFilter uploadFilter) {
this.uploadFilter = uploadFilter;
}
public void setDownloadFilter(RemoteResourceFilter downloadFilter) {
this.downloadFilter = (this.downloadFilter == null) ? defaultRemoteFilter : downloadFilter;
this.downloadFilter = downloadFilter;
}
public FileFilter getUploadFilter() {
public LocalFileFilter getUploadFilter() {
return uploadFilter;
}
@@ -92,9 +89,9 @@ public class SFTPFileTransfer
private final TransferListener listener = getTransferListener();
private void download(final RemoteResourceInfo remote, final File local)
private void download(final RemoteResourceInfo remote, final LocalDestFile local)
throws IOException {
final File adjustedFile;
final LocalDestFile adjustedFile;
switch (remote.getAttributes().getType()) {
case DIRECTORY:
listener.startedDir(remote.getName());
@@ -103,7 +100,7 @@ public class SFTPFileTransfer
break;
case UNKNOWN:
log.warn("Server did not supply information about the type of file at `{}` " +
"-- assuming it is a regular file!", remote.getPath());
"-- assuming it is a regular file!", remote.getPath());
case REGULAR:
listener.startedFile(remote.getName(), remote.getAttributes().getSize());
adjustedFile = downloadFile(remote, local);
@@ -116,30 +113,33 @@ public class SFTPFileTransfer
}
private File downloadDir(final RemoteResourceInfo remote, final File local)
private LocalDestFile downloadDir(final RemoteResourceInfo remote, final LocalDestFile local)
throws IOException {
final File adjusted = FileTransferUtil.getTargetDirectory(local, remote.getName());
final RemoteDirectory rd = sftp.openDir(remote.getPath());
final LocalDestFile adjusted = local.getTargetDirectory(remote.getName());
final RemoteDirectory rd = engine.openDir(remote.getPath());
try {
for (RemoteResourceInfo rri : rd.scan(getDownloadFilter()))
download(rri, new File(adjusted.getPath(), rri.getName()));
download(rri, adjusted.getChild(rri.getName()));
} finally {
rd.close();
}
return adjusted;
}
private File downloadFile(final RemoteResourceInfo remote, final File local)
private LocalDestFile downloadFile(final RemoteResourceInfo remote, final LocalDestFile local)
throws IOException {
final File adjusted = FileTransferUtil.getTargetFile(local, remote.getName());
final RemoteFile rf = sftp.open(remote.getPath());
final LocalDestFile adjusted = local.getTargetFile(remote.getName());
final RemoteFile rf = engine.open(remote.getPath());
try {
final FileOutputStream fos = new FileOutputStream(adjusted);
final OutputStream os = adjusted.getOutputStream();
try {
StreamCopier.copy(rf.getInputStream(), fos, sftp.getSubsystem()
.getLocalMaxPacketSize(), false, listener);
new StreamCopier(rf.getInputStream(), os)
.bufSize(engine.getSubsystem().getLocalMaxPacketSize())
.keepFlushing(false)
.listener(listener)
.copy();
} finally {
fos.close();
os.close();
}
} finally {
rf.close();
@@ -147,13 +147,13 @@ public class SFTPFileTransfer
return adjusted;
}
private void copyAttributes(final RemoteResourceInfo remote, final File local)
private void copyAttributes(final RemoteResourceInfo remote, final LocalDestFile local)
throws IOException {
final FileAttributes attrs = remote.getAttributes();
getModeSetter().setPermissions(local, attrs.getMode().getPermissionsMask());
if (getModeSetter().preservesTimes() && attrs.has(FileAttributes.Flag.ACMODTIME)) {
getModeSetter().setLastAccessedTime(local, attrs.getAtime());
getModeSetter().setLastModifiedTime(local, attrs.getMtime());
local.setPermissions(attrs.getMode().getPermissionsMask());
if (attrs.has(FileAttributes.Flag.ACMODTIME)) {
local.setLastAccessedTime(attrs.getAtime());
local.setLastModifiedTime(attrs.getMtime());
}
}
@@ -163,7 +163,7 @@ public class SFTPFileTransfer
private final TransferListener listener = getTransferListener();
private void upload(File local, String remote)
private void upload(LocalSourceFile local, String remote)
throws IOException {
final String adjustedPath;
if (local.isDirectory()) {
@@ -171,33 +171,36 @@ public class SFTPFileTransfer
adjustedPath = uploadDir(local, remote);
listener.finishedDir();
} else if (local.isFile()) {
listener.startedFile(local.getName(), local.length());
listener.startedFile(local.getName(), local.getLength());
adjustedPath = uploadFile(local, remote);
listener.finishedFile();
} else
throw new IOException(local + " is not a file or directory");
sftp.setAttributes(adjustedPath, getAttributes(local));
engine.setAttributes(adjustedPath, getAttributes(local));
}
private String uploadDir(File local, String remote)
private String uploadDir(LocalSourceFile local, String remote)
throws IOException {
final String adjusted = prepareDir(local, remote);
for (File f : local.listFiles(getUploadFilter()))
for (LocalSourceFile f : local.getChildren(getUploadFilter()))
upload(f, adjusted);
return adjusted;
}
private String uploadFile(File local, String remote)
private String uploadFile(LocalSourceFile local, String remote)
throws IOException {
final String adjusted = prepareFile(local, remote);
final RemoteFile rf = sftp.open(adjusted, EnumSet.of(OpenMode.WRITE,
OpenMode.CREAT,
OpenMode.TRUNC));
final RemoteFile rf = engine.open(adjusted, EnumSet.of(OpenMode.WRITE,
OpenMode.CREAT,
OpenMode.TRUNC));
try {
final FileInputStream fis = new FileInputStream(local);
final InputStream fis = local.getInputStream();
try {
final int bufSize = sftp.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead();
StreamCopier.copy(fis, rf.getOutputStream(), bufSize, false, listener);
new StreamCopier(fis, rf.getOutputStream())
.bufSize(engine.getSubsystem().getRemoteMaxPacketSize() - rf.getOutgoingPacketOverhead())
.keepFlushing(false)
.listener(listener)
.copy();
} finally {
fis.close();
}
@@ -207,37 +210,37 @@ public class SFTPFileTransfer
return adjusted;
}
private String prepareDir(File local, String remote)
private String prepareDir(LocalSourceFile local, String remote)
throws IOException {
final FileAttributes attrs;
try {
attrs = sftp.stat(remote);
attrs = engine.stat(remote);
} catch (SFTPException e) {
if (e.getStatusCode() == StatusCode.NO_SUCH_FILE) {
log.debug("probeDir: {} does not exist, creating", remote);
sftp.makeDir(remote);
engine.makeDir(remote);
return remote;
} else
throw e;
}
if (attrs.getMode().getType() == FileMode.Type.DIRECTORY)
if (pathHelper.getComponents(remote).getName().equals(local.getName())) {
if (engine.getPathHelper().getComponents(remote).getName().equals(local.getName())) {
log.debug("probeDir: {} already exists", remote);
return remote;
} else {
log.debug("probeDir: {} already exists, path adjusted for {}", remote, local.getName());
return prepareDir(local, PathComponents.adjustForParent(remote, local.getName()));
return prepareDir(local, engine.getPathHelper().adjustForParent(remote, local.getName()));
}
else
throw new IOException(attrs.getMode().getType() + " file already exists at " + remote);
}
private String prepareFile(File local, String remote)
private String prepareFile(LocalSourceFile local, String remote)
throws IOException {
final FileAttributes attrs;
try {
attrs = sftp.stat(remote);
attrs = engine.stat(remote);
} catch (SFTPException e) {
if (e.getStatusCode() == StatusCode.NO_SUCH_FILE) {
log.debug("probeFile: {} does not exist", remote);
@@ -247,7 +250,7 @@ public class SFTPFileTransfer
}
if (attrs.getMode().getType() == FileMode.Type.DIRECTORY) {
log.debug("probeFile: {} was directory, path adjusted for {}", remote, local.getName());
remote = PathComponents.adjustForParent(remote, local.getName());
remote = engine.getPathHelper().adjustForParent(remote, local.getName());
return remote;
} else {
log.debug("probeFile: {} is a {} file that will be replaced", remote, attrs.getMode().getType());
@@ -255,12 +258,11 @@ public class SFTPFileTransfer
}
}
private FileAttributes getAttributes(File local)
private FileAttributes getAttributes(LocalSourceFile local)
throws IOException {
final FileAttributes.Builder builder = new FileAttributes.Builder()
.withPermissions(getModeGetter().getPermissions(local));
if (getModeGetter().preservesTimes())
builder.withAtimeMtime(getModeGetter().getLastAccessTime(local), getModeGetter().getLastModifiedTime(local));
final FileAttributes.Builder builder = new FileAttributes.Builder().withPermissions(local.getPermissions());
if (local.providesAtimeMtime())
builder.withAtimeMtime(local.getLastAccessTime(), local.getLastModifiedTime());
return builder.build();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,27 +33,37 @@ public class SFTPPacket<T extends SFTPPacket<T>>
putByte(pt.toByte());
}
public FileAttributes readFileAttributes() {
public FileAttributes readFileAttributes()
throws SFTPException {
final FileAttributes.Builder builder = new FileAttributes.Builder();
final int mask = readInt();
if (FileAttributes.Flag.SIZE.isSet(mask))
builder.withSize(readUINT64());
if (FileAttributes.Flag.UIDGID.isSet(mask))
builder.withUIDGID(readInt(), readInt());
if (FileAttributes.Flag.MODE.isSet(mask))
builder.withPermissions(readInt());
if (FileAttributes.Flag.ACMODTIME.isSet(mask))
builder.withAtimeMtime(readInt(), readInt());
if (FileAttributes.Flag.EXTENDED.isSet(mask)) {
final int extCount = readInt();
for (int i = 0; i < extCount; i++)
builder.withExtended(readString(), readString());
try {
final int mask = readUInt32AsInt();
if (FileAttributes.Flag.SIZE.isSet(mask))
builder.withSize(readUInt64());
if (FileAttributes.Flag.UIDGID.isSet(mask))
builder.withUIDGID(readUInt32AsInt(), readUInt32AsInt());
if (FileAttributes.Flag.MODE.isSet(mask))
builder.withPermissions(readUInt32AsInt());
if (FileAttributes.Flag.ACMODTIME.isSet(mask))
builder.withAtimeMtime(readUInt32AsInt(), readUInt32AsInt());
if (FileAttributes.Flag.EXTENDED.isSet(mask)) {
final int extCount = readUInt32AsInt();
for (int i = 0; i < extCount; i++)
builder.withExtended(readString(), readString());
}
} catch (BufferException be) {
throw new SFTPException(be);
}
return builder.build();
}
public PacketType readType() {
return PacketType.fromByte(readByte());
public PacketType readType()
throws SFTPException {
try {
return PacketType.fromByte(readByte());
} catch (BufferException be) {
throw new SFTPException(be);
}
}
public T putFileAttributes(FileAttributes fa) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,8 @@
*/
package net.schmizz.sshj.sftp;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import net.schmizz.sshj.xfer.LocalDestFile;
import net.schmizz.sshj.xfer.LocalSourceFile;
import java.io.IOException;
import java.util.List;
@@ -26,15 +27,15 @@ public class StatefulSFTPClient
private String cwd;
public StatefulSFTPClient(SessionFactory ssh)
public StatefulSFTPClient(SFTPEngine engine)
throws IOException {
super(ssh);
super(engine);
this.cwd = getSFTPEngine().canonicalize(".");
log.info("Start dir = " + cwd);
}
private synchronized String cwdify(String path) {
return PathComponents.adjustForParent(cwd, path);
return engine.getPathHelper().adjustForParent(cwd, path);
}
public synchronized void cd(String dirname)
@@ -180,10 +181,22 @@ public class StatefulSFTPClient
super.get(cwdify(source), dest);
}
@Override
public void get(String source, LocalDestFile dest)
throws IOException {
super.get(cwdify(source), dest);
}
@Override
public void put(String source, String dest)
throws IOException {
super.get(source, cwdify(dest));
super.put(source, cwdify(dest));
}
@Override
public void put(LocalSourceFile source, String dest)
throws IOException {
super.put(source, cwdify(dest));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -90,9 +90,9 @@ public abstract class AbstractSignature
| sig[i++] & 0x000000ff;
i += j;
j = sig[i++] << 24 & 0xff000000
| sig[i++] << 16 & 0x00ff0000
| sig[i++] << 8 & 0x0000ff00
| sig[i++] & 0x000000ff;
| sig[i++] << 16 & 0x00ff0000
| sig[i++] << 8 & 0x0000ff00
| sig[i++] & 0x000000ff;
byte[] newSig = new byte[j];
System.arraycopy(sig, i, newSig, 0, j);
sig = newSig;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ import net.schmizz.sshj.transport.mac.MAC;
* <p/>
* <pre>
* Each packet is in the following format:
* <p/>
*
* uint32 packet_length
* byte padding_length
* byte[n1] payload; n1 = packet_length - padding_length - 1

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
*/
package net.schmizz.sshj.transport;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.ByteArrayUtils;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.SSHException;
@@ -87,7 +88,7 @@ final class Decoder
int need;
/* Decoding loop */
for (; ;)
for (; ; )
if (packetLength == -1) // Waiting for beginning of packet
{
@@ -123,7 +124,7 @@ final class Decoder
if (log.isTraceEnabled())
log.trace("Received packet #{}: {}", seq, plain.printHex());
packetHandler.handle(plain.readMessageID(), plain); // Process the decoded packet //
packetHandler.handle(plain.readMessageID(), plain); // Process the decoded packet
inputBuffer.clear();
packetLength = -1;
@@ -157,7 +158,12 @@ final class Decoder
throws TransportException {
cipher.update(inputBuffer.array(), 0, cipherSize);
final int len = inputBuffer.readInt(); // Read packet length
final int len; // Read packet length
try {
len = inputBuffer.readUInt32AsInt();
} catch (Buffer.BufferException be) {
throw new TransportException(be);
}
if (isInvalidPacketLength(len)) { // Check packet length validity
log.info("Error decoding packet (invalid length) {}", inputBuffer.printHex());

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2010, 2011 sshj contributors
*
* Licensed 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.transport;
import net.schmizz.sshj.common.DisconnectReason;
public interface DisconnectListener {
void notifyDisconnect(DisconnectReason reason);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,7 +63,7 @@ final class Encoder
private SSHPacket checkHeaderSpace(SSHPacket buffer) {
if (buffer.rpos() < 5) {
log.warn("Performance cost: when sending a packet, ensure that "
+ "5 bytes are available in front of the buffer");
+ "5 bytes are available in front of the buffer");
SSHPacket nb = new SSHPacket(buffer.available() + 5);
nb.rpos(5);
nb.wpos(5);
@@ -96,8 +96,6 @@ final class Encoder
long encode(SSHPacket buffer) {
encodeLock.lock();
try {
buffer = checkHeaderSpace(buffer);
if (log.isTraceEnabled())
log.trace("Encoding packet #{}: {}", seq, buffer.printHex());
@@ -116,7 +114,7 @@ final class Encoder
// Put packet header
buffer.wpos(startOfPacket);
buffer.putInt(packetLen);
buffer.putUInt32(packetLen);
buffer.putByte((byte) padLen);
// Now wpos will mark end of padding

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,8 +50,6 @@ final class Heartbeater
private int interval;
private boolean started;
Heartbeater(TransportImpl trans) {
this.trans = trans;
setName("heartbeater");
@@ -59,36 +57,36 @@ final class Heartbeater
synchronized void setInterval(int interval) {
this.interval = interval;
if (interval != 0) {
if (!started)
start();
notify();
}
if (interval > 0 && getState() == Thread.State.NEW)
start();
notify();
}
synchronized int getInterval() {
return interval;
}
synchronized private int getPositiveInterval()
throws InterruptedException {
while (interval <= 0)
wait();
return interval;
}
@Override
public void run() {
log.debug("Starting");
try {
while (!Thread.currentThread().isInterrupted()) {
int hi;
synchronized (this) {
while ((hi = interval) == 0)
wait();
}
if (!started)
started = true;
else if (trans.isRunning()) {
while (!isInterrupted()) {
final int hi = getPositiveInterval();
if (trans.isRunning()) {
log.info("Sending heartbeat since {} seconds elapsed", hi);
trans.write(new SSHPacket(Message.IGNORE));
}
Thread.sleep(hi * 1000);
}
} catch (Exception e) {
if (Thread.currentThread().isInterrupted()) {
if (isInterrupted()) {
// We are meant to shut up and draw to a close if interrupted
} else
trans.die(e);
@@ -96,4 +94,5 @@ final class Heartbeater
log.debug("Stopping");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,9 +35,18 @@
*/
package net.schmizz.sshj.transport;
import net.schmizz.concurrent.ErrorDeliveryUtil;
import net.schmizz.concurrent.Event;
import net.schmizz.concurrent.FutureUtils;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.ErrorNotifiable;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.SSHPacketHandler;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.Compression;
import net.schmizz.sshj.transport.digest.Digest;
@@ -47,6 +56,7 @@ import net.schmizz.sshj.transport.verification.HostKeyVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Arrays;
@@ -92,8 +102,8 @@ final class KeyExchanger
private Proposal clientProposal;
private NegotiatedAlgorithms negotiatedAlgs;
private final Event<TransportException> kexInitSent = new Event<TransportException>("kexinit sent",
TransportException.chainer);
private final Event<TransportException> kexInitSent =
new Event<TransportException>("kexinit sent", TransportException.chainer);
private final Event<TransportException> done;
@@ -209,11 +219,11 @@ final class KeyExchanger
return;
}
throw new TransportException(DisconnectReason.HOST_KEY_NOT_VERIFIABLE, "Could not verify `"
+ KeyType
.fromKey(key) + "` host key with fingerprint `" + SecurityUtils.getFingerprint(key)
+ "` for `" + transport
.getRemoteHost() + "` on port " + transport.getRemotePort());
throw new TransportException(DisconnectReason.HOST_KEY_NOT_VERIFIABLE,
"Could not verify `" + KeyType.fromKey(key)
+ "` host key with fingerprint `" + SecurityUtils.getFingerprint(key)
+ "` for `" + transport.getRemoteHost()
+ "` on port " + transport.getRemotePort());
}
private void setKexDone() {
@@ -224,14 +234,16 @@ final class KeyExchanger
private void gotKexInit(SSHPacket buf)
throws TransportException {
Proposal serverProposal = new Proposal(buf);
buf.rpos(buf.rpos() - 1);
final Proposal serverProposal = new Proposal(buf);
negotiatedAlgs = clientProposal.negotiate(serverProposal);
log.debug("Negotiated algorithms: {}", negotiatedAlgs);
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(), negotiatedAlgs
.getKeyExchangeAlgorithm());
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(),
negotiatedAlgs.getKeyExchangeAlgorithm());
try {
kex.init(transport, transport.getServerID().getBytes(), transport.getClientID().getBytes(), buf
.getCompactData(), clientProposal.getPacket().getCompactData());
kex.init(transport,
transport.getServerID(), transport.getClientID(),
serverProposal.getPacket().getCompactData(), clientProposal.getPacket().getCompactData());
} catch (GeneralSecurityException e) {
throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, e);
}
@@ -249,7 +261,7 @@ final class KeyExchanger
*
* @return the resized key
*/
private static byte[] resizedKey(byte[] E, int blockSize, Digest hash, byte[] K, byte[] H) {
private static byte[] resizedKey(byte[] E, int blockSize, Digest hash, BigInteger K, byte[] H) {
while (blockSize > E.length) {
Buffer.PlainBuffer buffer = new Buffer.PlainBuffer().putMPInt(K).putRawBytes(H).putRawBytes(E);
hash.update(buffer.array(), 0, buffer.available());
@@ -267,13 +279,15 @@ final class KeyExchanger
private void gotNewKeys() {
final Digest hash = kex.getHash();
final byte[] H = kex.getH();
if (sessionID == null)
// session id is 'H' from the first key exchange and does not change thereafter
sessionID = Arrays.copyOf(kex.getH(), kex.getH().length);
sessionID = H;
final Buffer.PlainBuffer hashInput = new Buffer.PlainBuffer()
.putMPInt(kex.getK())
.putRawBytes(kex.getH())
.putRawBytes(H)
.putByte((byte) 0) // <placeholder>
.putRawBytes(sessionID);
final int pos = hashInput.available() - sessionID.length - 1; // Position of <placeholder>
@@ -322,10 +336,12 @@ final class KeyExchanger
negotiatedAlgs.getServer2ClientMACAlgorithm());
mac_S2C.init(integrityKey_S2C);
final Compression compression_S2C = Factory.Named.Util.create(transport.getConfig().getCompressionFactories(),
negotiatedAlgs.getServer2ClientCompressionAlgorithm());
final Compression compression_C2S = Factory.Named.Util.create(transport.getConfig().getCompressionFactories(),
negotiatedAlgs.getClient2ServerCompressionAlgorithm());
final Compression compression_S2C =
Factory.Named.Util.create(transport.getConfig().getCompressionFactories(),
negotiatedAlgs.getServer2ClientCompressionAlgorithm());
final Compression compression_C2S =
Factory.Named.Util.create(transport.getConfig().getCompressionFactories(),
negotiatedAlgs.getClient2ServerCompressionAlgorithm());
transport.getEncoder().setAlgorithms(cipher_C2S, mac_C2S, compression_C2S);
transport.getDecoder().setAlgorithms(cipher_S2C, mac_S2C, compression_S2C);
@@ -345,7 +361,6 @@ final class KeyExchanger
* having sent the packet ourselves (would cause gotKexInit() to fail)
*/
kexInitSent.await(transport.getTimeout(), TimeUnit.SECONDS);
buf.rpos(buf.rpos() - 1);
gotKexInit(buf);
expected = Expected.FOLLOWUP;
break;
@@ -382,7 +397,7 @@ final class KeyExchanger
@Override
public void notifyError(SSHException error) {
log.debug("Got notified of {}", error.toString());
FutureUtils.alertAll(error, kexInitSent, done);
ErrorDeliveryUtil.alertEvents(error, kexInitSent, done);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -92,15 +92,15 @@ public final class NegotiatedAlgorithms {
@Override
public String toString() {
return ("[ " + //
"kex=" + kex + "; " + //
"sig=" + sig + "; " + //
"c2sCipher=" + c2sCipher + "; " + //
"s2cCipher=" + s2cCipher + "; " + //
"c2sMAC=" + c2sMAC + "; " + //
"s2cMAC=" + s2cMAC + "; " + //
"c2sComp=" + c2sComp + "; " + //
"s2cComp=" + s2cComp + //
return ("[ " +
"kex=" + kex + "; " +
"sig=" + sig + "; " +
"c2sCipher=" + c2sCipher + "; " +
"s2cCipher=" + s2cCipher + "; " +
"c2sMAC=" + c2sMAC + "; " +
"s2cMAC=" + s2cMAC + "; " +
"c2sComp=" + c2sComp + "; " +
"s2cComp=" + s2cComp +
" ]");
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@
package net.schmizz.sshj.transport;
import net.schmizz.sshj.Config;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHPacket;
@@ -82,21 +83,26 @@ class Proposal {
packet.putString("");
packet.putBoolean(false); // Optimistic next packet does not follow
packet.putInt(0); // "Reserved" for future by spec
packet.putUInt32(0); // "Reserved" for future by spec
}
public Proposal(SSHPacket packet) {
public Proposal(SSHPacket packet)
throws TransportException {
this.packet = packet;
final int savedPos = packet.rpos();
packet.rpos(packet.rpos() + 17); // Skip message ID & cookie
kex = fromCommaString(packet.readString());
sig = fromCommaString(packet.readString());
c2sCipher = fromCommaString(packet.readString());
s2cCipher = fromCommaString(packet.readString());
c2sMAC = fromCommaString(packet.readString());
s2cMAC = fromCommaString(packet.readString());
c2sComp = fromCommaString(packet.readString());
s2cComp = fromCommaString(packet.readString());
try {
kex = fromCommaString(packet.readString());
sig = fromCommaString(packet.readString());
c2sCipher = fromCommaString(packet.readString());
s2cCipher = fromCommaString(packet.readString());
c2sMAC = fromCommaString(packet.readString());
s2cMAC = fromCommaString(packet.readString());
c2sComp = fromCommaString(packet.readString());
s2cComp = fromCommaString(packet.readString());
} catch (Buffer.BufferException be) {
throw new TransportException(be);
}
packet.rpos(savedPos);
}
@@ -139,14 +145,14 @@ class Proposal {
public NegotiatedAlgorithms negotiate(Proposal other)
throws TransportException {
return new NegotiatedAlgorithms(
firstMatch(this.getKeyExchangeAlgorithms(), other.getKeyExchangeAlgorithms()), //
firstMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()), //
firstMatch(this.getClient2ServerCipherAlgorithms(), other.getClient2ServerCipherAlgorithms()), //
firstMatch(this.getServer2ClientCipherAlgorithms(), other.getServer2ClientCipherAlgorithms()), //
firstMatch(this.getClient2ServerMACAlgorithms(), other.getClient2ServerMACAlgorithms()), //
firstMatch(this.getServer2ClientMACAlgorithms(), other.getServer2ClientMACAlgorithms()), //
firstMatch(this.getClient2ServerCompressionAlgorithms(), other.getClient2ServerCompressionAlgorithms()), //
firstMatch(this.getServer2ClientCompressionAlgorithms(), other.getServer2ClientCompressionAlgorithms()) //
firstMatch(this.getKeyExchangeAlgorithms(), other.getKeyExchangeAlgorithms()),
firstMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()),
firstMatch(this.getClient2ServerCipherAlgorithms(), other.getClient2ServerCipherAlgorithms()),
firstMatch(this.getServer2ClientCipherAlgorithms(), other.getServer2ClientCipherAlgorithms()),
firstMatch(this.getClient2ServerMACAlgorithms(), other.getClient2ServerMACAlgorithms()),
firstMatch(this.getServer2ClientMACAlgorithms(), other.getServer2ClientMACAlgorithms()),
firstMatch(this.getClient2ServerCompressionAlgorithms(), other.getClient2ServerCompressionAlgorithms()),
firstMatch(this.getServer2ClientCompressionAlgorithms(), other.getServer2ClientCompressionAlgorithms())
);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
* Copyright 2010, 2011 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,22 +41,20 @@ import org.slf4j.LoggerFactory;
import java.io.InputStream;
final class Reader
public final class Reader
extends Thread {
private final Logger log = LoggerFactory.getLogger(getClass());
private final TransportImpl trans;
Reader(TransportImpl trans) {
public Reader(TransportImpl trans) {
this.trans = trans;
setName("reader");
}
@Override
public void run() {
final Thread curThread = Thread.currentThread();
try {
final Decoder decoder = trans.getDecoder();
@@ -66,7 +64,7 @@ final class Reader
int needed = 1;
while (!curThread.isInterrupted()) {
while (!isInterrupted()) {
int read = inp.read(recvbuf, 0, needed);
if (read == -1)
throw new TransportException("Broken transport; encountered EOF");
@@ -75,7 +73,7 @@ final class Reader
}
} catch (Exception e) {
if (curThread.isInterrupted()) {
if (isInterrupted()) {
// We are meant to shut up and draw to a close if interrupted
} else
trans.die(e);

Some files were not shown because too many files have changed in this diff Show More