Compare commits

..

336 Commits

Author SHA1 Message Date
hierynomus
936c6c8008 v0.11.0 2015-01-23 10:01:50 +01:00
hierynomus
8c0967ca93 Preparing for 0.11.0 release 2015-01-22 12:40:53 +01:00
hierynomus
cb5b7f0943 Setting up integration testing 2015-01-22 09:47:11 +01:00
hierynomus
de2ede05e7 Added gradle wrapper 2015-01-21 10:27:26 +01:00
hierynomus
4a90f99c5f Not sending EOF on OutputStream close (reopens #143) 2015-01-21 10:25:28 +01:00
hierynomus
e348f698e6 test logging 2015-01-20 19:05:15 +01:00
hierynomus
d59efaa5f9 Fixed KeepAliveRunner for when service not yet set 2015-01-20 17:13:47 +01:00
hierynomus
83c5f2f815 Removed java version check from build file as it breaks project import 2015-01-20 09:32:38 +01:00
hierynomus
b17d3fe867 Fixed some warnings 2015-01-20 09:31:49 +01:00
hierynomus
3cefda5bd3 Processed review comments from @demobox. Thx! 2015-01-20 09:31:24 +01:00
hierynomus
18f364a283 Made signing optional 2015-01-19 22:09:41 +01:00
hierynomus
d68032a9b8 Improved test logging 2015-01-19 21:55:41 +01:00
hierynomus
c73ba8bfa7 Merge branch 'gradle' 2015-01-19 21:32:47 +01:00
hierynomus
7bbfd40627 Moved examples to own 'project' 2015-01-19 21:30:34 +01:00
hierynomus
7ae84be548 Fixed build by adding license headers to new files 2015-01-19 11:54:55 +01:00
hierynomus
f2793d1acf Added osgi bundling info 2015-01-19 10:48:53 +01:00
hierynomus
bca5883422 Gradle build files 2015-01-19 10:23:25 +01:00
hierynomus
3e54e2c955 Merge branch 'heartbeat' 2015-01-19 10:06:23 +01:00
hierynomus
a7802ddcde Added keep-alive mechanism that detects disconnects (Fixes #166) 2015-01-19 10:05:03 +01:00
hierynomus
a7872b394b Added braces and log message to HeartBeater 2015-01-16 10:33:25 +01:00
Jeroen van Erp
3ade3977ef Merge pull request #165 from Boris-de/fix_bsize_bug
Fix bsize bug
2015-01-12 09:51:06 +01:00
Jeroen van Erp
efb2c547f9 Merge pull request #164 from Boris-de/mac_unittests
added unit-tests for the "mac"-package
2015-01-12 09:49:56 +01:00
hierynomus
703a0df09d Revert "no biggie if we don't send EOF on ChannelOutputStream.close()"
This reverts commit d95586508d.
2015-01-12 09:18:42 +01:00
Boris Wachtmeister
73de5b7b08 bugfix: BaseMac would not use bsize in certain cases
The implementation of BaseMac would only take the bsize (size of the
hash) into account if the #doFinal(byte[], int) method was called.
Both other #doFinal methods would behave as if bsize==defbsize and
not cut the hash to the right size.
2015-01-11 21:00:16 +01:00
Boris Wachtmeister
665cbf078a added unit-tests for the "mac"-package 2015-01-11 20:56:36 +01:00
hierynomus
b3ea908996 Upgraded BouncyCastle to 1.51 (Fixes #142) 2015-01-09 14:53:24 +01:00
Jeroen van Erp
11da49a4e7 Merge pull request #141 from ziuchkovski/add-proxy-support
Add proxy support for SocketClient/SSHClient
2015-01-09 14:30:13 +01:00
Jeroen van Erp
5b1f9f2a7d Merge pull request #156 from Boris-de/fix_hmac-sha2
fixed block sizes for hmac-sha2-256 and hmac-sha2-512
2015-01-09 14:15:14 +01:00
hierynomus
268de458e3 Changed log levels (Fixes #161) 2015-01-09 13:45:17 +01:00
hierynomus
834f0f22cd Added gitignore 2015-01-09 13:43:38 +01:00
hierynomus
1daf456cbe Added optional OSGi resolution for jzlib (Fixes #162) 2015-01-09 13:37:01 +01:00
Jeroen van Erp
ea11d34ac8 Merge pull request #157 from Donnerbart/master
Fixed unbounded range in pom.xml
2015-01-09 13:28:25 +01:00
Jeroen van Erp
e961dc1b27 Merge pull request #163 from lichtin/master
Update README.adoc
2015-01-09 13:27:45 +01:00
lichtin
25fbff245f Update README.adoc
Fix link to Google group
2015-01-01 20:23:21 +01:00
Donnerbart
333c23e167 Fixed unbounded range in pom.xml:
[INFO] Failed to resolve artifact.

Unable to get dependency information: Unable to read the metadata file for artifact 'net.schmizz:sshj:jar': Invalid JDK version in profile 'doclint-java8-disable': Unbounded range: [1.8, for project net.schmizz:sshj
  net.schmizz:sshj:jar:0.10.1-SNAPSHOT
2014-11-17 13:37:34 +01:00
Boris Wachtmeister
cf32842d0d added hmac-sha2-256 and hmac-sha2-512 to the README 2014-11-16 17:33:19 +01:00
Boris Wachtmeister
70720de71b fixed block sizes for hmac-sha2-256 and hmac-sha2-512
Both MACs have to use larger block/digest sizes than SHA1.
Additionally the KEX must be changed so that it will resize
the keys "E" and "F" to get keys of the right size for those
MACs (according to section 7.2 of rfc4253)
2014-11-11 19:32:41 +01:00
hierynomus
44e1ce1358 Moved readme to asciidoc format 2014-10-27 10:53:43 +01:00
Jeroen van Erp
921f41f9de Update README.rst 2014-10-01 16:24:37 +02:00
hierynomus
34c4be848a removed default signing and staging plugins 2014-10-01 16:18:48 +02:00
hierynomus
d10e303b1a Added travis config 2014-10-01 16:12:27 +02:00
Shikhar Bhushan
46791c87f5 Merge pull request #148 from bluekeyes/bugfix/await-timeout
Use long for Channel#await timeout
2014-09-18 19:20:29 +05:30
Shikhar Bhushan
af0d873e5b Merge pull request #150 from dkocher/master
Change handle to byte[]. Fix interoperability issue with Tectia SSH Serv...
2014-09-18 19:20:13 +05:30
David Kocher
d37b54b1fd Change handle to byte[]. Fix interoperability issue with Tectia SSH Server. Refer to issue #54. 2014-09-10 11:10:42 +02:00
Billy Keyes
c4408ac6dd Use long for Channel#await duration
This matches the underlying method called by AbstractChannel and is the
standard for timeouts with a TimeUnit.
2014-09-02 15:00:51 -07:00
Bob Ziuchkovski
ebbf440304 Add proxy support for SocketClient/SSHClient 2014-08-13 18:58:45 -06:00
Shikhar Bhushan
ef5a54d33f Merge pull request #139 from dkocher/master
Add constructor with file offset.
2014-08-03 22:06:31 +05:30
David Kocher
66514836c8 Add constructor with file offset. 2014-07-29 14:17:51 +02:00
shikhar
e943d80049 onwards to 0.10.1-SNAPSHOT 2014-07-02 11:44:26 +05:30
shikhar
81931f3b7a restore accidently removed assembly plugin for examples jar 2014-07-02 11:42:15 +05:30
shikhar
b8bfc19ecf release plugin mgmt 2014-07-02 11:36:54 +05:30
shikhar
0cb62c6d44 next release will be 0.10.0 2014-07-02 11:17:40 +05:30
shikhar
0ccc57b5af consistent license headers using plugin 2014-07-02 11:13:22 +05:30
shikhar
4806b1d6c7 Read-ahead input stream moved to its own class, as it will not play nice with mark/reset/skip. Use it by defautl for SFTPFileTransfer.
Closes #76 - no longer a significant difference in scp & sftp transfer speed
2014-06-25 12:42:05 +05:30
shikhar
ecc1d06dc2 StreamCopier logging 2014-06-25 12:38:24 +05:30
shikhar
d95586508d no biggie if we don't send EOF on ChannelOutputStream.close() 2014-06-25 11:21:35 +05:30
shikhar
5ee2f0a417 get rid of over-zealous sending of channel EOF & close messages which was implemented with questionable synchronization
fixes #105

also relevant to #126 since AbstractChannel does not synchronize on
'this' anymore
2014-06-24 17:57:23 +05:30
shikhar
2a7278d239 some small tweaks to PKCS8KeyFile in relation to the PEMReader -> PEMParser transition 2014-06-24 14:21:40 +05:30
shikhar
0875417dde don't close underlying RemoteFile when closing streams of that file - reverts f34667521d 2014-06-24 14:20:04 +05:30
shikhar
0a3ad4f68f not handed over yet 2014-06-24 11:54:06 +05:30
shikhar
fe58ecdee5 Change CONTRIBUTORS to contain the relevant command for getting at this info 2014-06-22 17:25:01 +05:30
shikhar
264e10b40c support multiline password prompts #132 2014-06-22 17:21:54 +05:30
Shikhar Bhushan
a00015969b Merge pull request #136 from dkocher/master
HMAC-SHA2, PuTTY key format, concurrent read for downloads and bug fixes
2014-06-22 17:09:06 +05:30
David Kocher
d6c22fef55 ADd clirr-maven-plugin 2014-06-18 10:16:22 +02:00
David Kocher
9886facf42 Fix test. 2014-06-18 10:08:07 +02:00
David Kocher
01be48508d Throw SSHException for packet length exceeding max size. 2014-06-03 11:55:44 +02:00
David Kocher
bdc541c959 Format client identification. 2014-06-03 11:54:38 +02:00
David Kocher
f2ebbe288f Ignore socket timeout in read which occurs if we have set the timeout to > 0. We should continue reading from the stream unless the reader is interrupted. Note that with the default timeout set to 0, the reader thread will never return. 2014-05-16 22:21:00 +02:00
David Kocher
9297338195 Use plain server message. 2014-05-16 15:33:24 +02:00
David Kocher
a8d2ea2028 Add disconnect message from server. 2014-05-16 15:30:04 +02:00
David Kocher
f34667521d Close remote handles when closing stream. 2014-05-14 23:31:07 +02:00
David Kocher
77f5d7fdb8 Extract encode method for signature. Implement signing for ECDSA. 2014-05-14 12:33:46 +02:00
David Kocher
08d0e59b6b Logging 2014-05-14 11:06:33 +02:00
David Kocher
5c540b6889 Interrupt packet reader thread on close. 2014-05-14 11:03:46 +02:00
David Kocher
baa8c8e995 Merge branch 'master' of https://github.com/xardazz/sshj 2014-05-09 14:04:25 +02:00
David Kocher
f354fd6661 Implement read ahead to speed up transfer rates for downloads by a magnitude. 2014-05-09 13:43:52 +02:00
David Kocher
93f1543af8 Add PuTTY key file implementation. 2014-05-09 10:37:53 +02:00
David Kocher
63424657da Check index length. 2014-05-08 16:17:43 +02:00
David Kocher
131e85c4d0 Add write method to append single entry. 2014-05-07 17:02:18 +02:00
David Kocher
587684c6a8 Fix null pointer. 2014-05-07 17:01:45 +02:00
xardazz
66f67db21b Update KeyType.java
remove my common lib
2014-05-07 13:14:16 +04:00
David Kocher
3356f533d0 Add dependencyManagement. 2014-05-07 10:42:27 +02:00
David Kocher
97535bbcae Merge interfaces. 2014-05-07 10:37:14 +02:00
David Kocher
896b0ea288 Add provider with reader resource. 2014-05-06 21:22:12 +02:00
David Kocher
60d54fa5de Addendum 2014-05-06 21:21:23 +02:00
David Kocher
06e421e752 Extract formats. Add PuTTY to enum. 2014-05-06 15:41:35 +02:00
Alexey Gromov
b5796f5e74 fix version 2014-05-06 12:13:11 +04:00
Alexey Gromov
0f7355a277 add ecdsa 2014-05-06 12:09:50 +04:00
David Kocher
466ff99e1c Update BC dependency to 1.50. 2014-05-05 13:12:48 +02:00
David Kocher
1f992c3fae Ignore user auth banner in transport. 2014-05-05 13:12:10 +02:00
David Kocher
df6019accc Fix type of fileOffset to long. 2014-05-05 13:11:54 +02:00
David Kocher
fdb891b842 Add hmac-sha2-256 and hmac-sha2-512. 2014-05-05 13:11:27 +02:00
Shikhar Bhushan
5159a799df fix the fix for #90 - need to statExistence of targetCwd not cwd 2013-09-07 11:39:28 -04:00
shikhar
78e5a2e30e bump ident for 0.9.1 before i forget 2013-08-11 22:35:19 -04:00
shikhar
db22f08f97 [maven-release-plugin] prepare for next development iteration 2013-08-11 22:25:50 -04:00
shikhar
c8cfc796af [maven-release-plugin] prepare release v0.9.0 2013-08-11 22:25:46 -04:00
shikhar
d9c0c6725c for 0.9.0 2013-08-11 22:08:26 -04:00
shikhar
b2297c6b44 version bumps 2013-08-11 18:56:46 -04:00
shikhar
e10ad28f2f inherit from sonatype oss parent pom 2013-08-11 18:56:46 -04:00
shikhar
61fc00a90a fix javadoc warning 2013-08-11 18:56:46 -04:00
shikhar
c8ef7ff0ca 0.9.0 snapshot 2013-08-11 17:27:04 -04:00
shikhar
e6c4f6ae69 #90 - only update cwd state if stat succeeds 2013-08-11 17:16:40 -04:00
shikhar
3418df7a56 #114 - visibility issue 2013-08-11 16:04:49 -04:00
shikhar
0ddd1f38c5 Simplify the UserAuth.authenticate(..) interface, move the multi-auth-method trial-and-error into SSHClient API 2013-04-15 22:56:24 -04:00
shikhar
0ec6918d7a minor javadoc 2013-03-24 19:09:21 -04:00
shikhar
88a88c5dba change transport layer to use millisecond timeouts 2013-03-24 17:36:11 -04:00
shikhar
6656214803 change connection layer to use millisecond timeouts 2013-03-24 17:27:36 -04:00
shikhar
c781724028 whitespace 2013-03-24 14:55:09 -04:00
shikhar
eefaa26882 missing flush() during banner exchange 2013-03-24 14:54:38 -04:00
Shikhar Bhushan
0d52441f01 Add 'unconfirmed writes' feature to SFTP RemoteFileOutputStream, allowing for major speedups
Thanks to @romainreuillon for the idea and initial implementation! #97
2013-02-23 18:16:29 -05:00
Shikhar Bhushan
9539ff6b7a In SFTPEngine / Requester, move from using TimeUnit.SECONDS to TimeUnit.MILLISECONDS, and start using some more explicit naming 2013-02-23 16:26:37 -05:00
Shikhar Bhushan
1ced1d4fdc Get rid of Requester.doRequest(), replace with request() method that returns the response promise. Make getTimeout() part of the interface. 2013-02-23 16:22:28 -05:00
Shikhar Bhushan
77924fd0be Revert "Implement concurent write requests."
This reverts commit 9acff6202c.
2013-02-23 16:00:53 -05:00
Shikhar Bhushan
3f195649fa Merge pull request #98 from andreaturli/master
Updated bouncycastle dependency
2013-02-23 03:41:44 -08:00
Shikhar Bhushan
42a4358f5c Merge pull request #104 from mpoindexter/master
ArrayIndexOutOfBounds when writing to a SFTP RemoteFile's OutputStream with large buffer
2013-02-23 03:41:19 -08:00
mpoindexter
61ce0f4868 Fix ArrayIndexOutOfBounds when writing big buffer
If ChannelOutputStream.write(byte[], int, int) was called with a buffer larger 
than bufferSize the loop in that method would call DataBuffer.write with a small len
and a large off.  This would cause the calculation in line 90 to return a negative n
leading to a ArrayIndexOutOfBounds.  The offset should not be taken into account when
calculating the number of bytes to put in the buffer.
2013-02-21 21:05:20 -08:00
Shikhar Bhushan
777995af3b Merge pull request #97 from romainreuillon/master
Make write a lot faster
2013-01-06 12:30:14 -08:00
Andrea Turli
635cf88acd updatet bouncycastle dep to the latest version 2012-12-31 01:44:10 +01:00
Romain Reuillon
ce515fddcd Change the scope back to protected, the change was unwanted. 2012-12-12 08:33:32 +01:00
Romain Reuillon
9acff6202c Implement concurent write requests. 2012-12-12 08:29:26 +01:00
Shikhar Bhushan
cbd118e0b1 fix #84 - debug log good enough 2012-10-21 02:13:55 +05:30
Shikhar Bhushan
a8cf749d95 #87 - include full exception trace when logging transport death 2012-10-21 01:45:13 +05:30
Shikhar Bhushan
f3d4707ef0 fix #89 - use IllegalStateException from SSHClient when sanity-check assertions fail 2012-10-21 01:40:07 +05:30
Shikhar Bhushan
4c5da634ad don't do a looped cond.await(timeout, unit) as that handles spurious wakeups, and it'll be buggy if the wakeup is due to a call to clear() 2012-10-21 01:21:36 +05:30
Shikhar Bhushan
2fdafb76fd [maven-release-plugin] prepare for next development iteration 2012-07-08 09:55:32 -04:00
Shikhar Bhushan
80b164a299 [maven-release-plugin] prepare release v0.8.1 2012-07-08 09:55:22 -04:00
Shikhar Bhushan
75418f33b7 Next release to be 0.8.1 2012-07-08 09:50:43 -04:00
Shikhar Bhushan
732de2b605 make logs less chatty
#80
2012-07-05 00:10:40 +05:30
Shikhar Bhushan
4fb56b868f Per #77 use regex matching inside PasswordResponseProvider. Also remove the 'gaveAlready' state, we can leave such logic to the PasswordFinder to implement if needed. 2012-06-06 23:59:41 +01:00
Shikhar Bhushan
a877ec1448 AbstractChannel#close() should be no-op if already closed. Fixes #53. 2012-06-06 22:57:27 +01:00
Shikhar Bhushan
b44631ea97 Better naming for some AbstractChannel's lock/event members 2012-06-06 22:51:25 +01:00
Shikhar Bhushan
a50962ba2f Small cleanup 2012-05-19 11:18:30 +01:00
Shikhar Bhushan
e8215e4af2 Update NOTICE 2012-05-14 11:33:05 +02:00
Shikhar Bhushan
3c2bda3196 docfix - not part of the contract 2012-05-12 22:13:22 +01:00
Shikhar Bhushan
b13e22084b [maven-release-plugin] prepare for next development iteration 2012-05-12 21:48:45 +01:00
Shikhar Bhushan
e7ba0e1e26 [maven-release-plugin] prepare release v0.8.0 2012-05-12 21:48:39 +01:00
Shikhar Bhushan
f712720538 Update CONTRIBUTORS 2012-05-12 17:33:07 +02:00
Shikhar Bhushan
540708e540 Provide a preference SFTPFileTransfer.setPreserveAttributes() to not set file attributes post upload or download.
Should address issue #42 / pull request #43
2012-05-12 16:24:01 +01:00
Shikhar Bhushan
e4d3a1f866 Some documentation on the FileTransfer interface. 2012-05-12 16:03:57 +01:00
Shikhar Bhushan
33969340e2 small tweak 2012-05-12 14:37:07 +01:00
Shikhar Bhushan
d65df3c9bc - Move trailing slash removal from SFTPEngine.mkdirs() to PathHelper.getComponents()
- Try to make the PathHelper.getComponents() code clearer
- Added some tests for PathHelper.getComponents()
2012-05-12 14:28:28 +01:00
Shikhar Bhushan
d2b9248535 Decouple PathHelper and SFTPEngine, introduce Canonicalizer interface 2012-05-12 11:23:44 +01:00
Shikhar Bhushan
431be8e7c7 Lower the ceiling on max remote packet size (so we don't allocate huge buffers) & spell it out mor explicitly 2012-05-12 11:09:41 +01:00
Shikhar Bhushan
885c602ab8 Merge pull request #66 from UrsKR/trailingseparator
SFTP client no longer tries to create folders twice when path has trailing separator
2012-05-12 03:09:04 -07:00
Shikhar Bhushan
8262e8fc98 Update CONTRIBUTORS 2012-04-28 13:21:47 +02:00
Shikhar Bhushan
844c5d7f77 Merge pull request #73 from aledsage/Issue-72-Buffer-Too-Large
Issue 72: fix for infinite loop if allocate too large a buffer
2012-04-28 04:16:16 -07:00
Aled Sage
fb690c4fb0 Issue 72: fix for infinite loop if allocate too large a buffer (due to invalid packet size) 2012-04-26 11:43:29 +01:00
Shikhar Bhushan
ab04596a20 Merge pull request #69 from ludoza/patch-1
fixed simple example.
2012-04-15 03:00:18 -07:00
ludoza
9ffdc35f93 fixed simple example. 2012-04-11 18:02:40 +03:00
Shikhar Bhushan
93e23f4cfb Don't try to send a disconnect packet if never connected. Fixes GH-67. 2012-04-05 12:30:56 -04:00
Shikhar Bhushan
504637099d copyright 2012-04-05 12:30:55 -04:00
Shikhar Bhushan
cafd9217bf whitespace 2012-04-05 12:30:55 -04:00
Urs Reupke
c627fabebd MkDirs no longer tries to create folders twice when path has trailing slash. 2012-03-23 10:58:42 +01:00
Shikhar Bhushan
1c4781a65d Merge pull request #65 from ryantenney/logging-fix
Avoid string concatenation in log statements
2012-03-20 01:10:52 -07:00
Ryan Tenney
aac7af2827 Avoid string concatenation in log statements. 2012-03-19 13:34:38 -04:00
Shikhar Bhushan
11c286b9b9 . 2012-02-08 20:01:58 +00:00
Shikhar Bhushan
7fae513fd8 google group 2012-02-08 20:01:39 +00:00
Shikhar Bhushan
53ad9d2288 int->long 2012-02-06 23:36:18 +00:00
Shikhar Bhushan
ee07072846 Make window size a long, as it can be upto (2^32 - 1)
Fix for #57
2012-02-06 22:24:52 +00:00
Shikhar Bhushan
d38bbbcdf7 clearer... 2012-02-05 19:54:42 +00:00
Shikhar Bhushan
bc59c81dbc Refactor TransferListener interface to support thread-safe, immutable implementation.
Fix #56
2012-01-29 22:54:17 +00:00
Shikhar Bhushan
d70d37cf4e Version string update 2012-01-29 22:52:59 +00:00
Shikhar Bhushan
777d82912c Merge pull request #55 from hierynomus/default-tty
Set default tty to vt100, as specified in the javadoc
2012-01-26 13:52:39 -08:00
hierynomus
f5db3e1563 Set default tty to vt100, as specified in the javadoc 2012-01-19 10:19:19 +01:00
Shikhar Bhushan
7e524f5c6f Make disconnect() an operation that can be repeated without side-effects beyond the first call. 2012-01-08 10:20:46 +00:00
Shikhar Bhushan
dbb3f62e82 [maven-release-plugin] prepare for next development iteration 2012-01-08 09:32:47 +00:00
Shikhar Bhushan
16a363fef6 [maven-release-plugin] prepare release v0.7.0 2012-01-08 09:32:41 +00:00
Shikhar Bhushan
9b0d39a798 Remove the SFTPClient.getFileTansfer() method that has typo altogether as this is a simple change to make for clients. 2012-01-08 09:30:05 +00:00
Shikhar Bhushan
81e36153d7 wrapping 2012-01-08 09:10:07 +00:00
Shikhar Bhushan
3026be282a Refactored the local port forwarding API; give caller control over initializing and cleaning up the server socket used.
Also removed 'server socket factory' stuff from SocketClient.
2012-01-05 22:26:44 +00:00
Shikhar Bhushan
8eedeb25fa Merge pull request #50 from iocanel/master
Remove bouncycastle version range from OSGi metadata.
2012-01-02 06:52:50 -08:00
Ioannis Canellos
de11880648 Removed package version from bouncycastle imports 2012-01-02 15:04:12 +02:00
Shikhar Bhushan
1ff4772f3f update id string 2011-12-20 11:12:35 +00:00
Shikhar Bhushan
22a5ffe735 fix for #47 - should send data down rather than sitting around waiting for an adjustment if there is window space available 2011-12-20 10:41:49 +00:00
Shikhar Bhushan
7a77f85ced docfix 2011-12-20 10:37:32 +00:00
Shikhar Bhushan
0002fe8b40 Made some Buffer subclasses final 2011-12-19 22:52:41 +00:00
Shikhar Bhushan
3028e7f218 Fix logging of window adjustments 2011-12-19 21:56:44 +00:00
Shikhar Bhushan
333e1cb7b8 small cleanup 2011-12-04 19:10:34 +00:00
Shikhar Bhushan
945d430916 Removed deprecated Session.Command's methods - getOutputAsString() and getErrorAsString() 2011-12-04 18:26:05 +00:00
Shikhar Bhushan
73b903784a Next version will be 0.7.0 2011-12-04 18:24:00 +00:00
Shikhar Bhushan
7d53649a85 . 2011-12-04 18:23:15 +00:00
Shikhar Bhushan
e193db9a14 Fix example in SSHClient doc 2011-12-04 18:14:27 +00:00
Shikhar Bhushan
a942edb911 Add SFTPClient.getFileTransfer() and deprecate getFileTansfer() which has typo 2011-12-04 18:11:30 +00:00
Shikhar Bhushan
137a7f5956 (reformat) 2011-12-04 17:48:42 +00:00
Shikhar Bhushan
718ff503df Merge pull request #41 from hierynomus/known-hosts
OpenSSH Known hosts format re-implemented
2011-12-04 09:35:58 -08:00
Shikhar Bhushan
d933b2538e Upgrade maven-bundle-plugin to 2.3.6 to fix #37 [https://issues.apache.org/jira/browse/FELIX-3058] 2011-12-04 17:10:33 +00:00
Shikhar Bhushan
ea6f9ceed2 Correct version 2011-12-04 13:54:19 +00:00
rws
07c61b14e8 Change SocketClient to public so that SSHClient can be mocked for testing. 2011-12-04 21:51:58 +08:00
hierynomus
4b175e6938 Re-implemented OpenSSHKnownHostsVerifier to deal with the real format 2011-11-10 09:51:18 +01:00
Shikhar Bhushan
f7e47cffa0 [maven-release-plugin] prepare for next development iteration 2011-10-02 22:17:46 +01:00
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
238 changed files with 8086 additions and 4906 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.bat text eol=crlf

17
.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
# IntelliJ IDEA
.idea/
*.iml
*.ipr
*.iws
# Eclipe
.project
.classpath
.settings/
# Output dirs
target/
build/
.gradle/

1
.travis.yml Normal file
View File

@@ -0,0 +1 @@
language: java

1
CONTRIBUTORS Normal file
View File

@@ -0,0 +1 @@
git log --format='%aN <%aE>' | awk '{arr[$0]++} END{for (i in arr){print arr[i], i;}}' | sort -rn | cut -d\ -f2-

2
NOTICE
View File

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

106
README.adoc Normal file
View File

@@ -0,0 +1,106 @@
= sshj - SSHv2 library for Java
Jeroen van Erp
:sshj_groupid: com.hierynomus
:sshj_version: 0.11.0
:source-highlighter: pygments
image::https://travis-ci.org/hierynomus/sshj.svg?branch=master[]
To get started, have a look at one of the examples. Hopefully you will find the API pleasant to work with :)
== Getting SSHJ
To get SSHJ, you have two options:
. Add a dependency to SSHJ to your project.
. Build SSHJ yourself.
And, if you want, you can also run the SSHJ examples.
Binary releases of SSHJ are not provided here, but you can download it http://search.maven.org/#artifactdetails%7C{sshj_groupid}%7Csshj%7C{sshj_version}%7Cjar[straight from the Maven Central repository] if you want to.
== Depending on SSHJ
If you're building your project using Maven, you can add the following dependency to the `pom.xml`:
[source,xml,subs="verbatim,attributes"]
----
<dependency>
<groupId>{sshj_groupid}</groupId>
<artifactId>sshj</artifactId>
<version>{sshj_version}</version>
</dependency>
----
If your project is built using another build tool that uses the Maven Central repository, translate this dependency into the format used by your build tool.
== Building SSHJ
. Clone the Overthere repository.
. Ensure you have Java6 installed with the http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html[Unlimited strength Java Cryptography Extensions (JCE)].
. Run the command `./gradlew clean build`.
== Running the examples
In the `examples` directory, there is a separate Maven project that shows how the library can be used in some sample cases. If you want to run them, follow these guidelines:
. Install http://maven.apache.org/[Maven 2.2.1] or up.
. Clone the Overthere repository.
. Go into the `examples` directory and run the command `mvn eclipse:eclipse`.
. Import the `examples` project into Eclipse.
. Change the login details in the example classes (address, username and password) and run them!
== Features of the library include:
* reading known_hosts files for host key verification
* publickey, password and keyboard-interactive authentication
* command, subsystem and shell channels
* local and remote port forwarding
* scp + complete sftp version 0-3 implementation
== Supported algorithms
Implementations / adapters for the following algorithms are included:
ciphers::
`aes{128,192,256}-{cbc,ctr}`, `blowfish-cbc`, `3des-cbc`
key exchange::
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`
signatures::
`ssh-rsa`, `ssh-dss`
mac::
`hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512`
compression::
`zlib` and `zlib@openssh.com` (delayed zlib)
private key files::
`pkcs8` encoded (what openssh uses)
If you need something that is not included, it shouldn't be too hard to add (do contribute it!)
== Dependencies
Java 6+. http://www.slf4j.org/download.html[slf4j] is required. http://www.bouncycastle.org/java.html[bouncycastle] is highly recommended and required for using some of the crypto algorithms. http://www.jcraft.com/jzlib/[jzlib] is required for using zlib compression.
== Reporting bugs
Issue tracker: https://github.com/hierynomus/sshj/issues
== Discussion
Google Group: http://groups.google.com/group/sshj-users
== Contributing
Fork away!
== Release history
SSHJ 0.11.0 (No date set yet)::
* New maven coordinates `com.hierynomus:sshj:0.11.0` as https://github.com/hierynomus[@hierynomus] took over as maintainer of SSHJ
* Migrated build system to Gradle 2.2.1
* Merged https://github.com/hierynomus/sshj/issues/150[#150]: Fix for incorrect file handle on some SSH servers, fixes: https://github.com/hierynomus/sshj/issues/54[#54], https://github.com/hierynomus/sshj/issues/119[#119], https://github.com/hierynomus/sshj/issues/168[#168], https://github.com/hierynomus/sshj/issues/169[#169]
* Made `jzlib` optional in OSGi bundling, fixes: https://github.com/hierynomus/sshj/issues/162[#162]
* Improved some log levels, fixes: https://github.com/hierynomus/sshj/issues/161[#161]
* Merged https://github.com/hierynomus/sshj/issues/156[#156], https://github.com/hierynomus/sshj/issues/164[#164], https://github.com/hierynomus/sshj/issues/165[#165]: Fixed block sizes for `hmac-sha2-256` and `hmac-sha2-512`
* Merged https://github.com/hierynomus/sshj/issues/141[#141]: Add proxy support
* Merged https://github.com/hierynomus/sshj/issues/157[#157], https://github.com/hierynomus/sshj/issues/163[#163]: Doc and build fixes
* Upgraded BouncyCastle to 1.51, fixes: https://github.com/hierynomus/sshj/issues/142[#142]
* Implemented keep-alive with connection drop detection, fixes https://github.com/hierynomus/sshj/issues/166[#166]

View File

@@ -1,64 +0,0 @@
sshj - SSHv2 library for Java
==============================
To get started, have a look at one of the examples. Hopefully you will find the API pleasant to work with :)
Features of the library include:
* reading known_hosts files for host key verification
* password and publickey authentication
* command, subsystem and shell channels
* local and remote port forwarding
* scp + complete sftp version 3 implementation
Implementations of the following algorithms are included:
ciphers
``aes{128,192,256}-{cbc,ctr}``, ``blowfish-cbc``, ``3des-cbc``
key exchange
``diffie-hellman-group1-sha1``, ``diffie-hellman-group14-sha1``
signatures
``ssh-rsa``, ``ssh-dss``
mac
``hmac-md5``, ``hmac-md5-96``, ``hmac-sha1``, ``hmac-sha1-96``
compression
``zlib`` and ``zlib@openssh.com`` (delayed zlib)
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!)
Dependencies
-------------
slf4j_ is required. bouncycastle_ is highly recommended and required for using some of the crypto algorithms.
jzlib_ is required for using zlib compression.
Help and discussion
--------------------
There is a `google group`_.
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

177
build-publishing.gradle Normal file
View File

@@ -0,0 +1,177 @@
apply plugin: "java"
apply plugin: "maven-publish"
apply plugin: "signing"
group = "nl.javadude"
version = "0.10.1-SNAPSHOT"
repositories {
mavenCentral()
mavenLocal()
}
configurations {
compile {
transitive = false
}
pom
}
def bouncycastleVersion = "1.50"
dependencies {
compile "org.slf4j:slf4j-api:1.7.7"
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
compile "com.jcraft:jzlib:1.1.3"
testCompile "junit:junit:4.11"
testCompile "org.mockito:mockito-core:1.9.5"
testCompile "org.apache.sshd:sshd-core:0.11.0"
testRuntime "ch.qos.logback:logback-classic:1.1.2"
}
task javadocJar(type: Jar) {
classifier = 'javadoc'
from javadoc
}
task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.allSource
}
task generatePom(type: GenerateMavenPom) {
destination = file("$buildDir/generated-pom.xml")
}
artifacts {
archives javadocJar, sourcesJar
pom generatePom.destination
}
signing {
sign configurations.archives
}
task signPom(type: Sign) {
sign configurations.pom
}
def getSignatureFiles = {
def allFiles = project.tasks.signArchives.signatureFiles.collect { it }
def signedSources = allFiles.find { it.name.contains('-sources') }
def signedJavadoc = allFiles.find { it.name.contains('-javadoc') }
def signedJar = (allFiles - [signedSources, signedJavadoc])[0]
return [
[archive: signedSources, classifier: 'sources', extension: 'jar.asc'],
[archive: signedJavadoc, classifier: 'javadoc', extension: 'jar.asc'],
[archive: signedJar, classifier: null, extension: 'jar.asc']
]
}
def getPomSignature = {
return project.tasks.signPom.signatureFiles.collect{it}[0]
}
publishing {
publications {
gpgJars(MavenPublication) {
getSignatureFiles().each {signature ->
artifact (signature.archive) {
classifier = signature.classifier
extension = signature.extension
}
}
}
gpgPom(MavenPublication) {
artifact(getPomSignature()) {
classifier = null
extension = "pom.asc"
}
}
maven(MavenPublication) {
from components.java
artifact (javadocJar) {
classifier = 'javadoc'
}
artifact (sourcesJar) {
classifier = 'sources'
}
pom.withXml {
asNode().children().last() + {
resolveStrategy = Closure.DELEGATE_FIRST
name "sshj"
description "SSHv2 library for Java"
url "https://github.com/hierynomus/sshj"
inceptionYear "2009"
issueManagement {
system "github"
url "https://github.com/hierynomus/sshj/issues"
}
scm {
connection "scm:git:git://github.com/hierynomus/sshj.git"
developerConnection "scm:git:git@github.com:hierynomus/sshj.git"
url "https://github.com/hierynomus/sshj.git"
}
licenses {
license {
name "Apache 2"
url "http://www.apache.org/licenses/LICENSE-2.0.txt"
distribution "repo"
}
}
developers {
developer {
id "hierynomus"
name "Jeroen van Erp"
email "jeroen@javadude.nl"
roles {
role "Lead developer"
}
}
developer {
id "shikhar"
name "Shikhar Bhushan"
email "shikhar@schmizz.net"
url "http://schmizz.net"
roles {
role "Previous lead developer"
}
}
developer {
id "iterate"
name "David Kocher"
email "dkocher@iterate.ch"
organization "iterage GmbH"
organizationUrl "https://iterate.ch"
roles {
role "Developer"
}
}
}
}
}
}
}
repositories {
maven {
url "file:/${project.projectDir}/artifacts"
}
}
}
project.afterEvaluate { p ->
p.tasks.publishGpgPomPublicationToMavenRepository.dependsOn("generatePom", "signPom")
}
generatePom.configure {
pom = publishing.publications.getByName("maven").pom
}

158
build.gradle Normal file
View File

@@ -0,0 +1,158 @@
apply plugin: "java"
apply plugin: "maven"
apply plugin: "signing"
apply plugin: "osgi"
group = "com.hierynomus"
version = "0.11.0"
repositories {
mavenCentral()
mavenLocal()
}
configurations {
compile {
transitive = false
}
}
test {
include "**/*Test.*"
afterSuite { descriptor, result ->
if (descriptor.className != null) {
def indicator = "\u001B[32m✓\u001b[0m"
if (result.failedTestCount > 0) {
indicator = "\u001B[31m✘\u001b[0m"
}
logger.lifecycle("$indicator Test ${descriptor.name}; Executed: ${result.testCount}/\u001B[32m${result.successfulTestCount}\u001B[0m/\u001B[31m${result.failedTestCount}\u001B[0m")
}
}
}
def bouncycastleVersion = "1.51"
dependencies {
compile "org.slf4j:slf4j-api:1.7.7"
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion"
compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion"
compile "com.jcraft:jzlib:1.1.3"
testCompile "junit:junit:4.11"
testCompile "org.mockito:mockito-core:1.9.5"
testCompile "org.apache.sshd:sshd-core:0.11.0"
testRuntime "ch.qos.logback:logback-classic:1.1.2"
}
jar {
manifest {
instruction "Bundle-Description", "SSHv2 library for Java"
instruction "Bundle-License", "http://www.apache.org/licenses/LICENSE-2.0.txt"
instruction "Import-Package", "!net.schmizz.*"
instruction "Import-Package", "javax.crypto*"
instruction "Import-Package", "com.jcraft.jzlib*;version=\"[1.1,2)\";resolution:=optional"
instruction "Import-Package", "org.slf4j*;version=\"[1.7,5)\""
instruction "Import-Package", "org.bouncycastle*"
instruction "Import-Package", "*"
instruction "Export-Package", "net.schmizz.*"
}
}
task javadocJar(type: Jar) {
classifier = 'javadoc'
from javadoc
}
task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.allSource
}
artifacts {
archives javadocJar, sourcesJar
}
signing {
required { !version.contains("SNAPSHOT") && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
// This disables the pedantic doclint feature of JDK8
if (JavaVersion.current().isJava8Compatible()) {
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
}
uploadArchives {
if(project.hasProperty('sonatypeUsername')) {
repositories.mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
configuration = configurations.archives
repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2') {
authentication(userName: sonatypeUsername, password: sonatypePassword)
}
snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/') {
authentication(userName: sonatypeUsername, password: sonatypePassword)
}
pom.project {
name "sshj"
description "SSHv2 library for Java"
url "https://github.com/hierynomus/sshj"
inceptionYear "2009"
issueManagement {
system "github"
url "https://github.com/hierynomus/sshj/issues"
}
scm {
connection "scm:git:git://github.com/hierynomus/sshj.git"
developerConnection "scm:git:git@github.com:hierynomus/sshj.git"
url "https://github.com/hierynomus/sshj.git"
}
licenses {
license {
name "Apache 2"
url "http://www.apache.org/licenses/LICENSE-2.0.txt"
distribution "repo"
}
}
developers {
developer {
id "hierynomus"
name "Jeroen van Erp"
email "jeroen@javadude.nl"
roles {
role "Lead developer"
}
}
developer {
id "shikhar"
name "Shikhar Bhushan"
email "shikhar@schmizz.net"
url "http://schmizz.net"
roles {
role "Previous lead developer"
}
}
developer {
id "iterate"
name "David Kocher"
email "dkocher@iterate.ch"
organization "iterage GmbH"
organizationUrl "https://iterate.ch"
roles {
role "Developer"
}
}
}
}
}
}
}

101
examples/pom.xml Normal file
View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2009 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.schmizz</groupId>
<artifactId>sshj</artifactId>
<packaging>jar</packaging>
<version>0.10.0</version>
<name>sshj-examples</name>
<description>Examples for SSHv2 library for Java</description>
<url>http://github.com/hierynomus/sshj</url>
<inceptionYear>2015</inceptionYear>
<issueManagement>
<system>github</system>
<url>http://github.com/hierynomus/sshj/issues</url>
</issueManagement>
<scm>
<connection>scm:git:git://github.com/hierynomus/sshj.git</connection>
<developerConnection>scm:git:git@github.com:hierynomus/sshj.git</developerConnection>
<url>http://github.com/hierynomus/sshj</url>
</scm>
<licenses>
<license>
<name>Apache 2</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<dependencies>
<dependency>
<groupId>net.schmizz</groupId>
<artifactId>sshj</artifactId>
<version>0.10.0</version>
</dependency>
</dependencies>
<developers>
<developer>
<id>hierynomus</id>
<name>Jeroen van Erp</name>
<email>jeroen@hierynomus.com</email>
</developer>
<developer>
<id>shikhar</id>
<name>Shikhar Bhushan</name>
<email>shikhar@schmizz.net</email>
<url>http://schmizz.net</url>
</developer>
<developer>
<id>iterate</id>
<name>David Kocher</name>
<email>dkocher@iterate.ch</email>
<organization>iterate GmbH</organization>
<organizationUrl>https://iterate.ch</organizationUrl>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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,31 +13,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples;
package net.schmizz.sshj.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

@@ -0,0 +1,55 @@
/**
* Copyright 2009 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.examples;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.DefaultConfig;
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 net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/** This examples demonstrates how to setup keep-alive to detect connection dropping. */
public class KeepAlive {
public static void main(String... args)
throws IOException, InterruptedException {
DefaultConfig defaultConfig = new DefaultConfig();
defaultConfig.setKeepAliveProvider(KeepAliveProvider.KEEP_ALIVE);
final SSHClient ssh = new SSHClient(defaultConfig);
try {
ssh.addHostKeyVerifier(new PromiscuousVerifier());
ssh.connect(args[0]);
ssh.getConnection().getKeepAlive().setKeepAliveInterval(5); //every 60sec
ssh.authPassword(args[1], args[2]);
Session session = ssh.startSession();
session.allocateDefaultPTY();
new CountDownLatch(1).await();
try {
session.allocateDefaultPTY();
} finally {
session.close();
}
} finally {
ssh.disconnect();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples;
package net.schmizz.sshj.examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
/**
* This example demonstrates local port forwarding, i.e. when we listen on a particular address and port; and forward
@@ -41,8 +43,16 @@ public class LocalPF {
* _We_ listen on localhost:8080 and forward all connections on to server, which then forwards it to
* google.com:80
*/
ssh.newLocalPortForwarder(new InetSocketAddress("localhost", 8080), "google.com", 80)
.listen();
final LocalPortForwarder.Parameters params
= new LocalPortForwarder.Parameters("0.0.0.0", 8080, "google.com", 80);
final ServerSocket ss = new ServerSocket();
ss.setReuseAddress(true);
ss.bind(new InetSocketAddress(params.getLocalHost(), params.getLocalPort()));
try {
ssh.newLocalPortForwarder(params, ss).listen();
} finally {
ss.close();
}
} finally {
ssh.disconnect();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples;
package net.schmizz.sshj.examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder.Forward;
@@ -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 2009 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,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples;
package net.schmizz.sshj.examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.StreamCopier;
@@ -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 2009 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,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples;
package net.schmizz.sshj.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 2009 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,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples;
package net.schmizz.sshj.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 2009 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,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples;
package net.schmizz.sshj.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 2009 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,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples;
package net.schmizz.sshj.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 2009 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,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples;
package net.schmizz.sshj.examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.StreamCopier;
@@ -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();

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Wed Jan 21 09:17:25 CET 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip

164
gradlew vendored Executable file
View File

@@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

255
pom.xml
View File

@@ -1,28 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<!--
Copyright 2009 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.schmizz</groupId>
<artifactId>sshj</artifactId>
<packaging>jar</packaging>
<version>0.2.0</version>
<packaging>bundle</packaging>
<version>0.10.1-SNAPSHOT</version>
<name>sshj</name>
<description>SSHv2 library for Java</description>
<url>http://github.com/shikhar/sshj</url>
<url>http://github.com/hierynomus/sshj</url>
<inceptionYear>2009</inceptionYear>
<issueManagement>
<system>github</system>
<url>http://github.com/shikhar/sshj/issues</url>
<url>http://github.com/hierynomus/sshj/issues</url>
</issueManagement>
<scm>
<connection>scm:git:git://github.com/shikhar/sshj.git</connection>
<developerConnection>scm:git:git@github.com:shikhar/sshj.git</developerConnection>
<url>http://github.com/shikhar/sshj</url>
<connection>scm:git:git://github.com/hierynomus/sshj.git</connection>
<developerConnection>scm:git:git@github.com:hierynomus/sshj.git</developerConnection>
<url>http://github.com/hierynomus/sshj</url>
</scm>
<licenses>
@@ -33,58 +51,112 @@
</license>
</licenses>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.51</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.51</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.11</version>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15</artifactId>
<version>1.45</version>
<scope>provided</scope>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>
<version>1.0.7</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>0.4.0</version>
<version>0.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>0.9.20</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.20</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<developers>
<developer>
<id>hierynomus</id>
<name>Jeroen van Erp</name>
<email>jeroen@hierynomus.com</email>
</developer>
<developer>
<id>shikhar</id>
<name>Shikhar Bhushan</name>
<email>shikhar@schmizz.net</email>
<url>http://schmizz.net</url>
</developer>
<developer>
<id>iterate</id>
<name>David Kocher</name>
<email>dkocher@iterate.ch</email>
<organization>iterate GmbH</organization>
<organizationUrl>https://iterate.ch</organizationUrl>
</developer>
</developers>
<properties>
@@ -96,40 +168,16 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<excludes>
<exclude>examples/*.java</exclude>
</excludes>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.0</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-4</version>
<configuration>
<descriptors>
<descriptor>src/assemble/examples.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -142,6 +190,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
@@ -154,7 +203,80 @@
</execution>
</executions>
</plugin>
</plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.4.0</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>
!net.schmizz.*,
javax.crypto*,
com.jcraft.jzlib*;version="[1.1,2)";resolution:=optional,
org.slf4j*;version="[1.7,5)",
org.bouncycastle*,
*
</Import-Package>
<Export-Package>net.schmizz.*</Export-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>clirr-maven-plugin</artifactId>
<version>2.6.1</version>
</plugin>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>2.6</version>
<configuration>
<header>src/etc/license-header</header>
<properties>
<owner>sshj contributors</owner>
<email>sshj-users@googlegroups.com</email>
</properties>
<excludes>
<exclude>**/README</exclude>
<exclude>src/test/resources/**</exclude>
<exclude>src/main/resources/**</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.2</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
--> </plugins>
</build>
<distributionManagement>
@@ -176,58 +298,47 @@
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15</artifactId>
<version>1.45</version>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>
<version>1.0.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>0.9.20</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.20</version>
</dependency>
</dependencies>
</profile>
<profile>
<id>release-sign-artifacts</id>
<id>doclint-java8-disable</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
<jdk>[1.8,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.0</version>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<passphrase>${gpg.passphrase}</passphrase>
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<reporting>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>clirr-maven-plugin</artifactId>
<version>2.6.1</version>
</plugin>
</plugins>
</reporting>
</project>

View File

@@ -1,15 +0,0 @@
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>examples</id>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>src/main/java/examples</directory>
<includes/>
<outputDirectory>examples</outputDirectory>
</fileSet>
</fileSets>
</assembly>

13
src/etc/license-header Normal file
View File

@@ -0,0 +1,13 @@
Copyright ${project.inceptionYear} ${owner}
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.

View File

@@ -0,0 +1,39 @@
package nl.javadude.sshj.connection.channel;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import static org.hamcrest.MatcherAssert.assertThat;
public class ChannelCloseEofTest {
private SSHClient sshClient;
@Before
public void setUp() throws Exception {
sshClient = new SSHClient();
}
@After
public void tearDown() throws IOException {
sshClient.disconnect();
}
@Test
public void shouldCorrectlyHandleSessionChannelEof() throws IOException, InterruptedException {
sshClient.addHostKeyVerifier(new PromiscuousVerifier());
sshClient.connect("172.16.37.129");
sshClient.authPassword("jeroen", "jeroen");
Session session = sshClient.startSession();
session.allocateDefaultPTY();
session.close();
Thread.sleep(1000);
assertThat("Should still be connected", sshClient.isConnected());
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright 2009 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 2009 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 2009 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,240 @@
/**
* Copyright 2009 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);
if (timeout == 0) {
while (val == null && pendingEx == null) {
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 was fulfilled with either a value or an error. */
public boolean isFulfilled() {
lock.lock();
try {
return pendingEx != null || val != 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

@@ -0,0 +1,34 @@
/**
* Copyright 2009 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.keepalive;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.connection.ConnectionImpl;
import net.schmizz.sshj.transport.TransportException;
final class Heartbeater
extends KeepAlive {
Heartbeater(ConnectionImpl conn) {
super(conn, "heartbeater");
}
@Override
protected void doKeepAlive() throws TransportException {
conn.getTransport().write(new SSHPacket(Message.IGNORE));
}
}

View File

@@ -0,0 +1,81 @@
/**
* Copyright 2009 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.keepalive;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.ConnectionImpl;
import net.schmizz.sshj.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class KeepAlive extends Thread {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final ConnectionImpl conn;
protected int keepAliveInterval = 0;
protected KeepAlive(ConnectionImpl conn, String name) {
this.conn = conn;
setName(name);
}
public synchronized int getKeepAliveInterval() {
return keepAliveInterval;
}
public synchronized void setKeepAliveInterval(int keepAliveInterval) {
this.keepAliveInterval = keepAliveInterval;
if (keepAliveInterval > 0 && getState() == State.NEW) {
start();
}
notify();
}
synchronized protected int getPositiveInterval()
throws InterruptedException {
while (keepAliveInterval <= 0) {
wait();
}
return keepAliveInterval;
}
@Override
public void run() {
log.debug("Starting {}, sending keep-alive every {} seconds", getClass().getSimpleName(), keepAliveInterval);
try {
while (!isInterrupted()) {
final int hi = getPositiveInterval();
if (conn.getTransport().isRunning()) {
log.debug("Sending keep-alive since {} seconds elapsed", hi);
doKeepAlive();
}
Thread.sleep(hi * 1000);
}
} catch (Exception e) {
// If we weren't interrupted, kill the transport, then this exception was unexpected.
// Else we're in shutdown-mode already, so don't forcibly kill the transport.
if (!isInterrupted()) {
conn.getTransport().die(e);
}
}
log.debug("Stopping {}", getClass().getSimpleName());
}
protected abstract void doKeepAlive() throws TransportException, ConnectionException;
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright 2009 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.keepalive;
import net.schmizz.sshj.connection.ConnectionImpl;
public abstract class KeepAliveProvider {
public static final KeepAliveProvider HEARTBEAT = new KeepAliveProvider() {
@Override
public KeepAlive provide(ConnectionImpl connection) {
return new Heartbeater(connection);
}
};
public static final KeepAliveProvider KEEP_ALIVE = new KeepAliveProvider() {
@Override
public KeepAlive provide(ConnectionImpl connection) {
return new KeepAliveRunner(connection);
}
};
public abstract KeepAlive provide(ConnectionImpl connection);
}

View File

@@ -0,0 +1,76 @@
/**
* Copyright 2009 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.keepalive;
import net.schmizz.concurrent.Promise;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.ConnectionImpl;
import net.schmizz.sshj.transport.TransportException;
import java.util.LinkedList;
import java.util.Queue;
import static java.lang.String.format;
import static net.schmizz.sshj.common.DisconnectReason.CONNECTION_LOST;
public class KeepAliveRunner extends KeepAlive {
/** The max number of keep-alives that should be unanswered before killing the connection. */
private int maxAliveCount = 5;
/** The queue of promises. */
private final Queue<Promise<SSHPacket, ConnectionException>> queue =
new LinkedList<Promise<SSHPacket, ConnectionException>>();
KeepAliveRunner(ConnectionImpl conn) {
super(conn, "keep-alive");
}
synchronized public int getMaxAliveCount() {
return maxAliveCount;
}
synchronized public void setMaxAliveCount(int maxAliveCount) {
this.maxAliveCount = maxAliveCount;
}
@Override
protected void doKeepAlive() throws TransportException, ConnectionException {
// Ensure the service is set... This means that the key exchange is done and the connection is up.
if (conn.equals(conn.getTransport().getService())) {
emptyQueue(queue);
checkMaxReached(queue);
queue.add(conn.sendGlobalRequest("keepalive@openssh.com", true, new byte[0]));
}
}
private void checkMaxReached(Queue<Promise<SSHPacket, ConnectionException>> queue) throws ConnectionException {
if (queue.size() >= maxAliveCount) {
throw new ConnectionException(CONNECTION_LOST,
format("Did not receive any keep-alive response for %s seconds", maxAliveCount * keepAliveInterval));
}
}
private void emptyQueue(Queue<Promise<SSHPacket, ConnectionException>> queue) {
Promise<SSHPacket, ConnectionException> peek = queue.peek();
while (peek != null && peek.isFulfilled()) {
log.debug("Received response from server to our keep-alive.");
queue.remove();
peek = queue.peek();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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,13 +35,10 @@ public abstract class AbstractService
protected final String name;
/** Transport layer */
protected final Transport trans;
/** Timeout for blocking operations */
protected int timeout;
public AbstractService(String name, Transport trans) {
this.name = name;
this.trans = trans;
timeout = trans.getTimeout();
}
@Override
@@ -57,7 +54,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 +63,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 {
@@ -83,12 +74,6 @@ public abstract class AbstractService
trans.reqService(this);
}
public int getTimeout() {
return this.timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 2009 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;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.cipher.Cipher;
@@ -31,6 +32,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}.
*
@@ -53,16 +55,16 @@ public interface Config {
List<Factory.Named<FileKeyProvider>> getFileKeyProviderFactories();
/**
* Retrieve the list of named factories for <code>KeyExchange</code>.
* Retrieve the list of named factories for {@code KeyExchange}.
*
* @return a list of named <code>KeyExchange</code> factories
* @return a list of named {@code KeyExchange} factories
*/
List<Factory.Named<KeyExchange>> getKeyExchangeFactories();
/**
* Retrieve the list of named factories for <code>MAC</code>.
* Retrieve the list of named factories for {@code MAC}.
*
* @return a list of named <code>MAC</code> factories
* @return a list of named {@code MAC} factories
*/
List<Factory.Named<MAC>> getMACFactories();
@@ -143,4 +145,14 @@ public interface Config {
*/
void setVersion(String version);
/**
* @return The provider that creates the keep-alive implementation of choice.
*/
KeepAliveProvider getKeepAliveProvider();
/**
* Set the provider that provides the keep-alive implementation.
* @param keepAliveProvider keep-alive provider
*/
void setKeepAliveProvider(KeepAliveProvider keepAliveProvider);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,29 +12,10 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.cipher.Cipher;
@@ -54,6 +35,7 @@ public class ConfigImpl
private String version;
private Factory<Random> randomFactory;
private KeepAliveProvider keepAliveProvider;
private List<Factory.Named<KeyExchange>> kexFactories;
private List<Factory.Named<Cipher>> cipherFactories;
@@ -166,4 +148,13 @@ public class ConfigImpl
this.version = version;
}
@Override
public KeepAliveProvider getKeepAliveProvider() {
return keepAliveProvider;
}
@Override
public void setKeepAliveProvider(KeepAliveProvider keepAliveProvider) {
this.keepAliveProvider = keepAliveProvider;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,33 +12,14 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.signature.SignatureDSA;
import net.schmizz.sshj.signature.SignatureECDSA;
import net.schmizz.sshj.signature.SignatureRSA;
import net.schmizz.sshj.transport.cipher.AES128CBC;
import net.schmizz.sshj.transport.cipher.AES128CTR;
@@ -56,11 +37,15 @@ import net.schmizz.sshj.transport.mac.HMACMD5;
import net.schmizz.sshj.transport.mac.HMACMD596;
import net.schmizz.sshj.transport.mac.HMACSHA1;
import net.schmizz.sshj.transport.mac.HMACSHA196;
import net.schmizz.sshj.transport.mac.HMACSHA2256;
import net.schmizz.sshj.transport.mac.HMACSHA2512;
import net.schmizz.sshj.transport.random.BouncyCastleRandom;
import net.schmizz.sshj.transport.random.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile;
import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -70,18 +55,23 @@ import java.util.LinkedList;
import java.util.List;
/**
* A {@link Config} that is initialized as follows. Items marked with an asterisk are added to the config only if
* A {@link net.schmizz.sshj.Config} that is initialized as follows. Items marked with an asterisk are added to the config only if
* BouncyCastle is in the classpath.
* <p/>
* <ul> <li>{@link ConfigImpl#setKeyExchangeFactories Key exchange}: {@link DHG14}*, {@link DHG1}</li> <li>{@link
* ConfigImpl#setCipherFactories Ciphers} [1]: {@link AES128CTR}, {@link AES192CTR}, {@link AES256CTR}, {@link
* AES128CBC}, {@link AES192CBC}, {@link AES256CBC}, {@link AES192CBC}, {@link TripleDESCBC}, {@link BlowfishCBC}</li>
* <li>{@link ConfigImpl#setMACFactories MAC}: {@link HMACSHA1}, {@link HMACSHA196}, {@link HMACMD5}, {@link
* HMACMD596}</li> <li>{@link ConfigImpl#setCompressionFactories Compression}: {@link NoneCompression}</li> <li>{@link
* ConfigImpl#setSignatureFactories Signature}: {@link SignatureRSA}, {@link SignatureDSA}</li> <li>{@link
* ConfigImpl#setRandomFactory PRNG}: {@link BouncyCastleRandom}* or {@link JCERandom}</li> <li>{@link
* ConfigImpl#setFileKeyProviderFactories Key file support}: {@link PKCS8KeyFile}*, {@link OpenSSHKeyFile}*</li>
* <li>{@link ConfigImpl#setVersion Client version}: {@code "NET_3_0"}</li> </ul>
* <ul>
* <li>{@link net.schmizz.sshj.ConfigImpl#setKeyExchangeFactories Key exchange}: {@link net.schmizz.sshj.transport.kex.DHG14}*, {@link net.schmizz.sshj.transport.kex.DHG1}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setCipherFactories Ciphers} [1]: {@link net.schmizz.sshj.transport.cipher.AES128CTR}, {@link net.schmizz.sshj.transport.cipher.AES192CTR}, {@link net.schmizz.sshj.transport.cipher.AES256CTR},
* {@link
* net.schmizz.sshj.transport.cipher.AES128CBC}, {@link net.schmizz.sshj.transport.cipher.AES192CBC}, {@link net.schmizz.sshj.transport.cipher.AES256CBC}, {@link net.schmizz.sshj.transport.cipher.AES192CBC}, {@link net.schmizz.sshj.transport.cipher.TripleDESCBC}, {@link net.schmizz.sshj.transport.cipher.BlowfishCBC}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setMACFactories MAC}: {@link net.schmizz.sshj.transport.mac.HMACSHA1}, {@link net.schmizz.sshj.transport.mac.HMACSHA196}, {@link net.schmizz.sshj.transport.mac.HMACMD5}, {@link
* net.schmizz.sshj.transport.mac.HMACMD596}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setCompressionFactories Compression}: {@link net.schmizz.sshj.transport.compression.NoneCompression}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setSignatureFactories Signature}: {@link net.schmizz.sshj.signature.SignatureRSA}, {@link net.schmizz.sshj.signature.SignatureDSA}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setRandomFactory PRNG}: {@link net.schmizz.sshj.transport.random.BouncyCastleRandom}* or {@link net.schmizz.sshj.transport.random.JCERandom}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setFileKeyProviderFactories Key file support}: {@link net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile}*, {@link
* net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile}*</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setVersion Client version}: {@code "NET_3_0"}</li>
* </ul>
* <p/>
* [1] It is worth noting that Sun's JRE does not have the unlimited cryptography extension enabled by default. This
* prevents using ciphers with strength greater than 128.
@@ -91,7 +81,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_9_2";
public DefaultConfig() {
setVersion(VERSION);
@@ -103,6 +93,7 @@ public class DefaultConfig
initCompressionFactories();
initMACFactories();
initSignatureFactories();
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
}
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
@@ -113,12 +104,13 @@ public class DefaultConfig
}
protected void initRandomFactory(boolean bouncyCastleRegistered) {
setRandomFactory(new SingletonRandomFactory(bouncyCastleRegistered ? new BouncyCastleRandom.Factory() : new JCERandom.Factory()));
setRandomFactory(new SingletonRandomFactory(bouncyCastleRegistered
? new BouncyCastleRandom.Factory() : new JCERandom.Factory()));
}
protected void initFileKeyProviderFactories(boolean bouncyCastleRegistered) {
if (bouncyCastleRegistered) {
setFileKeyProviderFactories(new PKCS8KeyFile.Factory(), new OpenSSHKeyFile.Factory());
setFileKeyProviderFactories(new PKCS8KeyFile.Factory(), new OpenSSHKeyFile.Factory(), new PuTTYKeyFile.Factory());
}
}
@@ -134,9 +126,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,21 +137,23 @@ 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);
}
protected void initSignatureFactories() {
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory());
setSignatureFactories(new SignatureECDSA.Factory(), new SignatureRSA.Factory(), new SignatureDSA.Factory());
}
protected void initMACFactories() {
setMACFactories(new HMACSHA1.Factory(), new HMACSHA196.Factory(), new HMACMD5.Factory(),
new HMACMD596.Factory());
new HMACMD596.Factory(), new HMACSHA2256.Factory(), new HMACSHA2512.Factory());
}
protected void initCompressionFactories() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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,10 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 +31,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;
@@ -46,6 +45,7 @@ import net.schmizz.sshj.userauth.UserAuth;
import net.schmizz.sshj.userauth.UserAuthException;
import net.schmizz.sshj.userauth.UserAuthImpl;
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
import net.schmizz.sshj.userauth.keyprovider.KeyFormat;
import net.schmizz.sshj.userauth.keyprovider.KeyPairWrapper;
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
import net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil;
@@ -61,12 +61,14 @@ 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;
import java.net.ServerSocket;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
@@ -91,17 +93,20 @@ import java.util.List;
* <em>A simple example:</em>
* <p/>
* <pre>
* client = new SSHClient();
* client.initUserKnownHosts();
* final SSHClient client = new SSHClient();
* client.loadKnownHosts();
* 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
* {
* client.disconnect();
* 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>
* <p/>
@@ -110,7 +115,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 +123,6 @@ public class SSHClient
/** Logger */
protected final Logger log = LoggerFactory.getLogger(getClass());
/** Transport layer */
protected final Transport trans;
@@ -140,9 +144,9 @@ public class SSHClient
*/
public SSHClient(Config config) {
super(DEFAULT_PORT);
this.trans = new TransportImpl(config);
this.trans = new TransportImpl(config, this);
this.auth = new UserAuthImpl(trans);
this.conn = new ConnectionImpl(trans);
this.conn = new ConnectionImpl(trans, config.getKeepAliveProvider());
}
/**
@@ -156,24 +160,24 @@ 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);
}
});
}
// FIXME: there are way too many auth... overrides. Better API needed.
/**
* Authenticate {@code username} using the supplied {@code methods}.
*
@@ -185,7 +189,7 @@ public class SSHClient
*/
public void auth(String username, AuthMethod... methods)
throws UserAuthException, TransportException {
assert isConnected();
checkConnected();
auth(username, Arrays.<AuthMethod>asList(methods));
}
@@ -200,8 +204,17 @@ public class SSHClient
*/
public void auth(String username, Iterable<AuthMethod> methods)
throws UserAuthException, TransportException {
assert isConnected();
auth.authenticate(username, (Service) conn, methods);
checkConnected();
final Deque<UserAuthException> savedEx = new LinkedList<UserAuthException>();
for (AuthMethod method: methods) {
try {
if (auth.authenticate(username, (Service) conn, method, trans.getTimeoutMs()))
return;
} catch (UserAuthException e) {
savedEx.push(e);
}
}
throw new UserAuthException("Exhausted available authentication methods", savedEx.peek());
}
/**
@@ -296,8 +309,7 @@ public class SSHClient
* @throws TransportException if there was a transport-layer error
*/
public void authPublickey(String username, Iterable<KeyProvider> keyProviders)
throws UserAuthException,
TransportException {
throws UserAuthException, TransportException {
final List<AuthMethod> am = new LinkedList<AuthMethod>();
for (KeyProvider kp : keyProviders)
am.add(new AuthPublickey(kp));
@@ -342,13 +354,14 @@ public class SSHClient
public void authPublickey(String username, String... locations)
throws UserAuthException, TransportException {
final List<KeyProvider> keyProviders = new LinkedList<KeyProvider>();
for (String loc : locations)
for (String loc : locations) {
try {
log.debug("Attempting to load key from: {}", loc);
keyProviders.add(loadKeys(loc));
} catch (IOException logged) {
log.warn("Could not load keys due to: {}", logged);
log.info("Could not load keys from {} due to: {}", loc, logged.getMessage());
}
}
authPublickey(username, keyProviders);
}
@@ -362,10 +375,8 @@ public class SSHClient
@Override
public void disconnect()
throws IOException {
assert isConnected();
trans.disconnect();
super.disconnect();
assert !isConnected();
}
/** @return the associated {@link Connection} instance. */
@@ -391,8 +402,7 @@ public class SSHClient
/**
* @return the associated {@link UserAuth} instance. This allows access to information like the {@link
* UserAuth#getBanner() authentication banner}, whether authentication was at least {@link
* UserAuth#hadPartialSuccess() partially successful}, and any {@link UserAuth#getSavedExceptions() saved
* exceptions} that were ignored because there were more authentication method that could be tried.
* UserAuth#hadPartialSuccess() partially successful}.
*/
public UserAuth getUserAuth() {
return auth;
@@ -441,7 +451,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
@@ -475,9 +485,9 @@ public class SSHClient
public KeyProvider loadKeys(String location, PasswordFinder passwordFinder)
throws IOException {
final File loc = new File(location);
final FileKeyProvider.Format format = KeyProviderUtil.detectKeyFileFormat(loc);
final FileKeyProvider fkp = Factory.Named.Util.create(trans.getConfig().getFileKeyProviderFactories(), format
.toString());
final KeyFormat format = KeyProviderUtil.detectKeyFileFormat(loc);
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(loc, passwordFinder);
@@ -501,6 +511,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 KeyFormat 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
@@ -541,23 +578,21 @@ public class SSHClient
}
/**
* Create a {@link LocalPortForwarder} that will listen on {@code address} and forward incoming connections to the
* server; which will further forward them to {@code host:port}.
* Create a {@link LocalPortForwarder} that will listen based on {@code parameters} using the bound
* {@code serverSocket} and forward incoming connections to the server; which will further forward them to
* {@code host:port}.
* <p/>
* The returned forwarder's {@link LocalPortForwarder#listen() listen()} method should be called to actually start
* listening, this method just creates an instance.
*
* @param address defines where the {@link LocalPortForwarder} listens
* @param host hostname to which the server will forward
* @param port the port at {@code hostname} to which the server wil forward
* @param parameters parameters for the forwarding setup
* @param serverSocket bound server socket
*
* @return a {@link LocalPortForwarder}
*
* @throws IOException if there is an error opening a local server socket
*/
public LocalPortForwarder newLocalPortForwarder(SocketAddress address, String host, int port)
throws IOException {
return new LocalPortForwarder(getServerSocketFactory(), conn, address, host, port);
public LocalPortForwarder newLocalPortForwarder(LocalPortForwarder.Parameters parameters,
ServerSocket serverSocket) {
return new LocalPortForwarder(conn, parameters, serverSocket);
}
/**
@@ -582,7 +617,8 @@ public class SSHClient
/** @return Instantiated {@link SCPFileTransfer} implementation. */
public SCPFileTransfer newSCPFileTransfer() {
assert isConnected() && isAuthenticated();
checkConnected();
checkAuthenticated();
return new SCPFileTransfer(this);
}
@@ -594,8 +630,9 @@ public class SSHClient
*/
public SFTPClient newSFTPClient()
throws IOException {
assert isConnected() && isAuthenticated();
return new SFTPClient(this);
checkConnected();
checkAuthenticated();
return new SFTPClient(new SFTPEngine(this).init());
}
/**
@@ -611,10 +648,10 @@ public class SSHClient
@Override
public Session startSession()
throws ConnectionException, TransportException {
assert isConnected() && isAuthenticated();
checkConnected();
checkAuthenticated();
final SessionChannel sess = new SessionChannel(conn);
sess.open();
assert sess.isOpen();
return sess;
}
@@ -654,18 +691,33 @@ public class SSHClient
*/
protected void doKex()
throws TransportException {
assert trans.isRunning();
checkConnected();
final long start = System.currentTimeMillis();
trans.doKex();
log.debug("Key exchange took {} seconds", (System.currentTimeMillis() - start) / 1000.0);
}
try {
trans.doKex();
} catch (TransportException te) {
trans.disconnect(DisconnectReason.KEY_EXCHANGE_FAILED);
throw te;
/**
* Same as {@link #disconnect()}.
*
* @throws IOException
*/
@Override
public void close()
throws IOException {
disconnect();
}
private void checkConnected() {
if (!isConnected()) {
throw new IllegalStateException("Not connected");
}
}
log.info("Key exchange took {} seconds", (System.currentTimeMillis() - start) / 1000.0);
private void checkAuthenticated() {
if (!isAuthenticated()) {
throw new IllegalStateException("Not authenticated");
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,40 +12,19 @@
* 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.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
abstract class SocketClient {
public abstract class SocketClient {
private final int defaultPort;
@@ -54,7 +33,6 @@ abstract class SocketClient {
private OutputStream output;
private SocketFactory socketFactory = SocketFactory.getDefault();
private ServerSocketFactory serverSocketFactory = ServerSocketFactory.getDefault();
private static final int DEFAULT_CONNECT_TIMEOUT = 0;
private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
@@ -74,12 +52,25 @@ abstract class SocketClient {
onConnect();
}
public void connect(InetAddress host, int port, Proxy proxy)
throws IOException {
socket = new Socket(proxy);
socket.connect(new InetSocketAddress(host, port), connectTimeout);
onConnect();
}
public void connect(String hostname, int port)
throws IOException {
this.hostname = hostname;
connect(InetAddress.getByName(hostname), port);
}
public void connect(String hostname, int port, Proxy proxy)
throws IOException {
this.hostname = hostname;
connect(InetAddress.getByName(hostname), port, proxy);
}
public void connect(InetAddress host, int port,
InetAddress localAddr, int localPort)
throws IOException {
@@ -106,6 +97,16 @@ abstract class SocketClient {
connect(hostname, defaultPort);
}
public void connect(InetAddress host, Proxy proxy)
throws IOException {
connect(host, defaultPort, proxy);
}
public void connect(String hostname, Proxy proxy)
throws IOException {
connect(hostname, defaultPort, proxy);
}
public void disconnect()
throws IOException {
if (socket != null) {
@@ -158,17 +159,6 @@ abstract class SocketClient {
return socketFactory;
}
public void setServerSocketFactory(ServerSocketFactory factory) {
if (factory == null)
serverSocketFactory = ServerSocketFactory.getDefault();
else
serverSocketFactory = factory;
}
public ServerSocketFactory getServerSocketFactory() {
return serverSocketFactory;
}
public int getConnectTimeout() {
return connectTimeout;
}
@@ -204,4 +194,4 @@ abstract class SocketClient {
output = socket.getOutputStream();
}
}
}

View File

@@ -1,3 +1,18 @@
/**
* Copyright 2009 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.common;
@@ -32,7 +47,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 +115,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 +214,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 +240,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 +308,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 +328,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 +339,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 +368,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 +384,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 +407,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 +698,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 +751,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 +769,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 +786,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 +861,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 +893,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 +908,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 +948,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 +981,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 +1110,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 +1170,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 +1223,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 +1337,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 +1369,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 +1385,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 +1424,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 +1458,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 +1520,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 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,26 +12,6 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.common;
@@ -44,13 +24,14 @@ 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);
}
}
public static class PlainBuffer
public static final class PlainBuffer
extends Buffer<PlainBuffer> {
public PlainBuffer() {
@@ -73,10 +54,15 @@ public class Buffer<T extends Buffer<T>> {
/** The default size for a {@code Buffer} (256 bytes) */
public static final int DEFAULT_SIZE = 256;
/** The maximum valid size of buffer (i.e. biggest power of two that can be represented as an int - 2^30) */
public static final int MAX_SIZE = (1 << 30);
protected static int getNextPowerOf2(int i) {
int j = 1;
while (j < i)
while (j < i) {
j <<= 1;
if (j <= 0) throw new IllegalArgumentException("Cannot get next power of 2; "+i+" is too large");
}
return j;
}
@@ -139,7 +125,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 +164,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 +185,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 +210,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 +241,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 +286,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 +308,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 +324,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 +363,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 +384,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 +398,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 +415,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 +423,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 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,48 +12,14 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.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 +41,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 +94,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 2009 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 2009 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 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,26 +12,6 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.common;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,39 +12,24 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.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 +40,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 2009 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,41 +15,49 @@
*/
package net.schmizz.sshj.common;
import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;
/** Type of key e.g. rsa, dsa */
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 +67,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;
@@ -87,9 +100,91 @@ public enum KeyType {
},
/** SSH identifier for ECDSA keys */
ECDSA("ecdsa-sha2-nistp256") {
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
throws GeneralSecurityException {
try {
// final String algo = buf.readString(); it has been already read
final String curveName = buf.readString();
final int keyLen = buf.readUInt32AsInt();
final byte x04 = buf.readByte(); // it must be 0x04, but don't think we need that check
final byte[] x = new byte[(keyLen - 1) / 2];
final byte[] y = new byte[(keyLen - 1) / 2];
buf.readRawBytes(x);
buf.readRawBytes(y);
if(log.isDebugEnabled()) {
log.debug(String.format("Key algo: %s, Key curve: %s, Key Len: %s, 0x04: %s\nx: %s\ny: %s",
type,
curveName,
keyLen,
x04,
Arrays.toString(x),
Arrays.toString(y))
);
}
if (!NISTP_CURVE.equals(curveName)) {
throw new GeneralSecurityException(String.format("Unknown curve %s", curveName));
}
BigInteger bigX = new BigInteger(1, x);
BigInteger bigY = new BigInteger(1, y);
X9ECParameters ecParams = NISTNamedCurves.getByName("p-256");
ECPoint pPublicPoint = ecParams.getCurve().createPoint(bigX, bigY, false);
ECParameterSpec spec = new ECParameterSpec(ecParams.getCurve(),
ecParams.getG(), ecParams.getN());
ECPublicKeySpec publicSpec = new ECPublicKeySpec(pPublicPoint, spec);
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA");
return keyFactory.generatePublic(publicSpec);
} catch (Exception ex) {
throw new GeneralSecurityException(ex);
}
}
@Override
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
final ECPublicKey ecdsa = (ECPublicKey) pk;
final java.security.spec.ECPoint point = ecdsa.getW();
final byte[] x = trimStartingZeros(point.getAffineX().toByteArray());
final byte[] y = trimStartingZeros(point.getAffineY().toByteArray());
buf.putString(sType)
.putString(NISTP_CURVE)
.putUInt32(1 + x.length + y.length)
.putRawBytes(new byte[] { (byte) 0x04 })
.putRawBytes(x)
.putRawBytes(y)
;
}
@Override
protected boolean isMyType(Key key) {
return ("ECDSA".equals(key.getAlgorithm()));
}
private byte[] trimStartingZeros(byte[] in) {
int i = 0;
for (; i < in.length; i++) {
if (in[i] != 0) {
break;
}
}
final byte[] out = new byte[in.length - i];
System.arraycopy(in, i, out, 0, out.length);
return out;
}
},
/** Unrecognized */
UNKNOWN("unknown") {
@Override
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
throws GeneralSecurityException {
@@ -105,10 +200,11 @@ public enum KeyType {
protected boolean isMyType(Key key) {
return false;
}
};
private static final String NISTP_CURVE = "nistp256";
protected final String sType;
private KeyType(String type) {
@@ -120,7 +216,6 @@ public enum KeyType {
public abstract void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
protected abstract boolean isMyType(Key key);
public static KeyType fromKey(Key key) {
@@ -142,4 +237,4 @@ public enum KeyType {
return sType;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,26 +12,6 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.common;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,32 +12,12 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.common;
import java.util.Arrays;
public class SSHPacket
public final class SSHPacket
extends Buffer<SSHPacket> {
public SSHPacket() {
@@ -75,12 +55,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 2009 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 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,26 +12,6 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.common;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,26 +12,6 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.common;
@@ -58,14 +38,15 @@ import java.security.Signature;
public class SecurityUtils {
private static class BouncyCastleRegistration {
public void run()
throws Exception {
if (java.security.Security.getProvider(BOUNCY_CASTLE) == null) {
LOG.info("Trying to register BouncyCastle as a JCE provider");
LOG.debug("Trying to register BouncyCastle as a JCE provider");
java.security.Security.addProvider(new BouncyCastleProvider());
MessageDigest.getInstance("MD5", BOUNCY_CASTLE);
KeyAgreement.getInstance("DH", BOUNCY_CASTLE);
LOG.info("Registration succeeded");
LOG.info("BouncyCastle registration succeeded");
} else
LOG.info("BouncyCastle already registered as a JCE provider");
securityProvider = BOUNCY_CASTLE;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 {}: {}", in, out, ioe);
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.debug("{} KiB transferred in {} seconds ({} KiB/s)", sizeKiB, 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 2009 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.connection;
import net.schmizz.concurrent.Future;
import net.schmizz.concurrent.Promise;
import net.schmizz.keepalive.KeepAlive;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.connection.channel.OpenFailException;
@@ -89,13 +90,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;
/**
@@ -125,29 +126,34 @@ public interface Connection {
void setMaxPacketSize(int maxPacketSize);
/** @return the size for the local window this connection recommends to any {@link Channel}'s that ask for it. */
int getWindowSize();
long getWindowSize();
/**
* Set the size for the local window this connection recommends to any {@link Channel}'s that ask for it.
*
* @param windowSize window size in bytes
*/
void setWindowSize(int windowSize);
void setWindowSize(long windowSize);
/** @return the associated {@link Transport}. */
Transport getTransport();
/**
* @return the {@code timeout} in seconds that this connection uses for blocking operations and recommends to any
* {@link Channel other} {@link ForwardedChannelOpener classes} that ask for it.
* @return the {@code timeout} in milliseconds that this connection uses for blocking operations and recommends to
* any {@link Channel other} {@link ForwardedChannelOpener classes} that ask for it.
*/
int getTimeout();
int getTimeoutMs();
/**
* Set the {@code timeout} this connection uses for blocking operations and recommends to any {@link Channel other}
* {@link ForwardedChannelOpener classes} that ask for it.
*
* @param timeout timeout in seconds
* @param timeout timeout in milliseconds
*/
void setTimeout(int timeout);
void setTimeoutMs(int timeout);
/**
* @return The configured {@link net.schmizz.keepalive.KeepAlive} mechanism.
*/
KeepAlive getKeepAlive();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 2009 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,18 @@
*/
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.keepalive.KeepAlive;
import net.schmizz.keepalive.KeepAliveProvider;
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,23 +51,32 @@ 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;
/** {@code keep-alive} mechanism */
private final KeepAlive keepAlive;
private long windowSize = 2048 * 1024;
private int maxPacketSize = 32 * 1024;
private volatile int timeoutMs;
/**
* Create with an associated {@link Transport}.
*
* @param config the ssh config
* @param trans transport layer
* @param keepAlive
*/
public ConnectionImpl(Transport trans) {
public ConnectionImpl(Transport trans, KeepAliveProvider keepAlive) {
super("ssh-connection", trans);
timeoutMs = trans.getTimeoutMs();
this.keepAlive = keepAlive.provide(this);
}
@Override
public void attach(Channel chan) {
log.info("Attaching `{}` channel (#{})", chan.getType(), chan.getID());
log.debug("Attaching `{}` channel (#{})", chan.getType(), chan.getID());
channels.put(chan.getID(), chan);
}
@@ -81,7 +92,7 @@ public class ConnectionImpl
@Override
public void forget(Channel chan) {
log.info("Forgetting `{}` channel (#{})", chan.getType(), chan.getID());
log.debug("Forgetting `{}` channel (#{})", chan.getType(), chan.getID());
channels.remove(chan.getID());
synchronized (internalSynchronizer) {
if (channels.isEmpty())
@@ -91,26 +102,30 @@ public class ConnectionImpl
@Override
public void forget(ForwardedChannelOpener opener) {
log.info("Forgetting opener for `{}` channels: {}", opener.getChannelType(), opener);
log.debug("Forgetting opener for `{}` channels: {}", opener.getChannelType(), opener);
openers.remove(opener.getChannelType());
}
@Override
public void attach(ForwardedChannelOpener opener) {
log.info("Attaching opener for `{}` channels: {}", opener.getChannelType(), opener);
log.debug("Attaching opener for `{}` channels: {}", opener.getChannelType(), opener);
openers.put(opener.getChannelType(), opener);
}
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 +154,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;
@@ -168,12 +170,12 @@ public class ConnectionImpl
}
@Override
public int getWindowSize() {
public long getWindowSize() {
return windowSize;
}
@Override
public void setWindowSize(int windowSize) {
public void setWindowSize(long windowSize) {
this.windowSize = windowSize;
}
@@ -192,46 +194,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) {
log.info("Making global request for `{}`", name);
synchronized (globalReqPromises) {
log.debug("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 +246,36 @@ 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();
}
keepAlive.interrupt();
ErrorNotifiable.Util.alertAll(error, channels.values());
channels.clear();
}
@Override
public void setTimeoutMs(int timeoutMs) {
this.timeoutMs = timeoutMs;
}
@Override
public int getTimeoutMs() {
return timeoutMs;
}
@Override
public KeepAlive getKeepAlive() {
return keepAlive;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,31 +12,11 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.connection.channel;
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;
@@ -61,8 +41,10 @@ import java.util.concurrent.locks.ReentrantLock;
public abstract class AbstractChannel
implements Channel {
private static final int REMOTE_MAX_PACKET_SIZE_CEILING = 1024 * 1024;
/** Logger */
protected final Logger log;
protected final Logger log = LoggerFactory.getLogger(getClass());
/** Transport layer */
protected final Transport trans;
@@ -79,15 +61,12 @@ public abstract class AbstractChannel
private final Queue<Event<ConnectionException>> chanReqResponseEvents = new LinkedList<Event<ConnectionException>>();
/* The lock used by to create the open & close events */
private final ReentrantLock lock = new ReentrantLock();
private final ReentrantLock openCloseLock = new ReentrantLock();
/** Channel open event */
protected final Event<ConnectionException> open;
protected final Event<ConnectionException> openEvent;
/** Channel close event */
private final Event<ConnectionException> close;
/* Access to these fields should be synchronized using this object */
private boolean eofSent;
private boolean eofGot;
protected final Event<ConnectionException> closeEvent;
/** Whether we have already sent a CHANNEL_CLOSE request to the server */
private boolean closeRequested;
/** Local window */
@@ -109,20 +88,18 @@ 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);
close = new Event<ConnectionException>("chan#" + id + " / " + "close", ConnectionException.chainer, lock);
openEvent = new Event<ConnectionException>("chan#" + id + " / " + "open", ConnectionException.chainer, openCloseLock);
closeEvent = new Event<ConnectionException>("chan#" + id + " / " + "close", ConnectionException.chainer, openCloseLock);
}
protected void init(int recipient, int remoteWinSize, int remoteMaxPacketSize) {
protected void init(int recipient, long remoteWinSize, long remoteMaxPacketSize) {
this.recipient = recipient;
rwin = new Window.Remote(id, remoteWinSize, remoteMaxPacketSize);
rwin = new Window.Remote(remoteWinSize, (int) Math.min(remoteMaxPacketSize, REMOTE_MAX_PACKET_SIZE_CEILING));
out = new ChannelOutputStream(this, trans, rwin);
log.info("Initialized - {}", this);
log.debug("Initialized - {}", this);
}
@Override
@@ -146,7 +123,7 @@ public abstract class AbstractChannel
}
@Override
public int getLocalWinSize() {
public long getLocalWinSize() {
return lwin.getSize();
}
@@ -166,7 +143,7 @@ public abstract class AbstractChannel
}
@Override
public int getRemoteWinSize() {
public long getRemoteWinSize() {
return rwin.getSize();
}
@@ -185,11 +162,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:
@@ -220,7 +197,7 @@ public abstract class AbstractChannel
private void gotClose()
throws TransportException {
log.info("Got close");
log.debug("Got close");
try {
closeAllStreams();
sendClose();
@@ -238,11 +215,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, openEvent, closeEvent);
ErrorDeliveryUtil.alertEvents(error, chanReqResponseEvents);
in.notifyError(error);
out.notifyError(error);
if (out != null)
out.notifyError(error);
finishOff();
}
@@ -255,65 +233,90 @@ public abstract class AbstractChannel
@Override
public void close()
throws ConnectionException, TransportException {
lock.lock();
openCloseLock.lock();
try {
try {
sendClose();
} catch (TransportException e) {
if (!close.hasError())
throw e;
if (isOpen()) {
try {
sendClose();
} catch (TransportException e) {
if (!closeEvent.inError())
throw e;
}
closeEvent.await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
}
close.await(conn.getTimeout(), TimeUnit.SECONDS);
} finally {
lock.unlock();
openCloseLock.unlock();
}
}
protected synchronized void sendClose()
public void join()
throws ConnectionException {
closeEvent.await();
}
public void join(long timeout, TimeUnit unit)
throws ConnectionException {
closeEvent.await(timeout, unit);
}
protected void sendClose()
throws TransportException {
openCloseLock.lock();
try {
if (!closeRequested) {
log.info("Sending close");
log.debug("Sending close");
trans.write(newBuffer(Message.CHANNEL_CLOSE));
}
} finally {
closeRequested = true;
openCloseLock.unlock();
}
}
@Override
public synchronized boolean isOpen() {
lock.lock();
public boolean isOpen() {
openCloseLock.lock();
try {
return open.isSet() && !close.isSet() && !closeRequested;
return openEvent.isSet() && !closeEvent.isSet() && !closeRequested;
} finally {
lock.unlock();
openCloseLock.unlock();
}
}
private void gotChannelRequest(SSHPacket buf)
throws ConnectionException, TransportException {
final String reqType = buf.readString();
buf.readBoolean(); // We don't care about the 'want-reply' value
log.info("Got chan request for `{}`", reqType);
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.debug("Got chan request for `{}`", reqType);
handleRequest(reqType, buf);
}
private void gotWindowAdjustment(int howMuch) {
log.info("Received window adjustment for {} bytes", howMuch);
private void gotWindowAdjustment(SSHPacket buf)
throws ConnectionException {
final long howMuch;
try {
howMuch = buf.readUInt32();
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
log.debug("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();
closeEvent.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,59 +329,65 @@ 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)
);
log.debug("Sending channel request for `{}`", reqType);
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()
private void gotEOF()
throws TransportException {
log.info("Got EOF");
eofGot = true;
log.debug("Got EOF");
eofInputStreams();
if (eofSent)
sendClose();
}
/** Called when EOF has been received. Subclasses can override but must call super. */
@@ -386,26 +395,10 @@ public abstract class AbstractChannel
in.eof();
}
@Override
public synchronized void sendEOF()
throws TransportException {
try {
if (!closeRequested && !eofSent) {
log.info("Sending EOF");
trans.write(newBuffer(Message.CHANNEL_EOF));
if (eofGot)
sendClose();
}
} finally {
eofSent = true;
out.setClosed();
}
}
@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 2009 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.
*
@@ -97,7 +99,7 @@ public interface Channel
int getLocalMaxPacketSize();
/** @return the current local window size. */
int getLocalWinSize();
long getLocalWinSize();
/** @return an {@code OutputStream} for this channel. */
OutputStream getOutputStream();
@@ -109,7 +111,7 @@ public interface Channel
int getRemoteMaxPacketSize();
/** @return the current remote window size. */
int getRemoteWinSize();
long getRemoteWinSize();
/** @return the channel type identifier. */
String getType();
@@ -117,15 +119,6 @@ public interface Channel
/** @return whether the channel is open. */
boolean isOpen();
/**
* Sends an EOF message to the server for this channel; indicating that no more data will be sent by us. The {@code
* OutputStream} for this channel will be closed and no longer usable.
*
* @throws TransportException if there is an error sending the EOF message
*/
void sendEOF()
throws TransportException;
/**
* Set whether local window should automatically expand when data is received, irrespective of whether data has been
* read from that stream. This is useful e.g. when a remote command produces a lot of output that would fill the
@@ -135,4 +128,10 @@ public interface Channel
*/
void setAutoExpand(boolean autoExpand);
void join()
throws ConnectionException;
void join(long timeout, TimeUnit unit)
throws ConnectionException;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,28 +12,7 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.connection.channel;
import net.schmizz.sshj.common.Buffer;
@@ -59,7 +38,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 +50,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 +95,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)
@@ -161,11 +138,11 @@ public final class ChannelInputStream
private void checkWindow()
throws TransportException {
synchronized (win) {
final int adjustment = win.neededAdjustment();
final long adjustment = win.neededAdjustment();
if (adjustment > 0) {
log.info("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST to #{} for {} bytes", chan.getRecipient(), adjustment);
log.debug("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 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,35 +12,17 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.connection.channel;
import net.schmizz.sshj.common.Buffer;
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.ConnectionException;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
import java.io.IOException;
import java.io.OutputStream;
@@ -56,26 +38,99 @@ public final class ChannelOutputStream
private final Channel chan;
private final Transport trans;
private final Window.Remote win;
private final SSHPacket buffer = new SSHPacket();
private final DataBuffer buffer = new DataBuffer();
private final byte[] b = new byte[1];
private int bufferLength;
private boolean closed;
private SSHException error;
private final class DataBuffer {
private final int headerOffset;
private final int dataOffset;
private final SSHPacket packet = new SSHPacket(Message.CHANNEL_DATA);
private final Buffer.PlainBuffer leftOvers = new Buffer.PlainBuffer();
DataBuffer() {
headerOffset = packet.rpos();
packet.putUInt32(0); // recipient
packet.putUInt32(0); // data length
dataOffset = packet.wpos();
}
int write(byte[] data, int off, int len)
throws TransportException, ConnectionException {
final int bufferSize = packet.wpos() - dataOffset;
if (bufferSize >= win.getMaxPacketSize()) {
flush(bufferSize, true);
return 0;
} else {
final int n = Math.min(len, win.getMaxPacketSize() - bufferSize);
packet.putRawBytes(data, off, n);
return n;
}
}
boolean flush(boolean canAwaitExpansion)
throws TransportException, ConnectionException {
return flush(packet.wpos() - dataOffset, canAwaitExpansion);
}
boolean flush(int bufferSize, boolean canAwaitExpansion)
throws TransportException, ConnectionException {
while (bufferSize > 0) {
long remoteWindowSize = win.getSize();
if (remoteWindowSize == 0) {
if (canAwaitExpansion) {
remoteWindowSize = win.awaitExpansion(remoteWindowSize);
} else {
return false;
}
}
// We can only write the min. of
// a) how much data we have
// b) the max packet size
// c) what the current window size will allow
final int writeNow = Math.min(bufferSize, (int) Math.min(win.getMaxPacketSize(), remoteWindowSize));
packet.wpos(headerOffset);
packet.putMessageID(Message.CHANNEL_DATA);
packet.putUInt32(chan.getRecipient());
packet.putUInt32(writeNow);
packet.wpos(dataOffset + writeNow);
final int leftOverBytes = bufferSize - writeNow;
if (leftOverBytes > 0) {
leftOvers.putRawBytes(packet.array(), packet.wpos(), leftOverBytes);
}
trans.write(packet);
win.consume(writeNow);
packet.rpos(headerOffset);
packet.wpos(dataOffset);
if (leftOverBytes > 0) {
packet.putBuffer(leftOvers);
leftOvers.clear();
}
bufferSize = leftOverBytes;
}
return true;
}
}
public ChannelOutputStream(Channel chan, Transport trans, Window.Remote win) {
this.chan = chan;
this.trans = trans;
this.win = win;
prepBuffer();
}
private void prepBuffer() {
bufferLength = 0;
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
}
@Override
@@ -86,19 +141,13 @@ public final class ChannelOutputStream
}
@Override
public synchronized void write(byte[] data, int off, int len)
public synchronized void write(final byte[] data, int off, int len)
throws IOException {
checkClose();
while (len > 0) {
final int x = Math.min(len, win.getMaxPacketSize() - bufferLength);
if (x <= 0) {
flush();
continue;
}
buffer.putRawBytes(data, off, x);
bufferLength += x;
off += x;
len -= x;
final int n = buffer.write(data, off, len);
off += n;
len -= n;
}
}
@@ -107,55 +156,40 @@ public final class ChannelOutputStream
this.error = error;
}
private synchronized void checkClose()
private void checkClose()
throws SSHException {
if (closed)
if (closed) {
if (error != null)
throw error;
else
throw new ConnectionException("Stream closed");
}
}
@Override
public synchronized void close()
throws IOException {
if (!closed)
if (!closed) {
try {
flush();
chan.sendEOF();
buffer.flush(false);
// trans.write(new SSHPacket(Message.CHANNEL_EOF).putUInt32(chan.getRecipient()));
} finally {
setClosed();
closed = true;
}
}
}
public synchronized void setClosed() {
closed = true;
}
/**
* Send all data currently buffered. If window space is exhausted in the process, this will block
* until it is expanded by the server.
*
* @throws IOException
*/
@Override
public synchronized void flush()
throws IOException {
checkClose();
if (bufferLength <= 0) // No data to send
return;
putRecipientAndLength();
try {
win.waitAndConsume(bufferLength);
trans.write(buffer);
} finally {
prepBuffer();
}
}
private void putRecipientAndLength() {
final int origPos = buffer.wpos();
buffer.wpos(6);
buffer.putInt(chan.getRecipient());
buffer.putInt(bufferLength);
buffer.wpos(origPos);
buffer.flush(true);
}
@Override
@@ -163,4 +197,4 @@ public final class ChannelOutputStream
return "< ChannelOutputStream for Channel #" + chan.getID() + " >";
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 2009 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 2009 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,24 +22,23 @@ 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();
protected final int maxPacketSize;
protected int size;
protected long size;
public Window(int chanID, String kindOfWindow, int initialWinSize, int maxPacketSize) {
log = LoggerFactory.getLogger("<< chan#" + chanID + " / " + kindOfWindow + " >>");
public Window(long initialWinSize, int maxPacketSize) {
size = initialWinSize;
this.maxPacketSize = maxPacketSize;
}
public void expand(int inc) {
public void expand(long inc) {
synchronized (lock) {
log.debug("Increasing by {} up to {}", inc, size);
size += inc;
log.debug("Increasing by {} up to {}", inc, size);
lock.notifyAll();
}
}
@@ -48,16 +47,19 @@ public abstract class Window {
return maxPacketSize;
}
public int getSize() {
return size;
public long getSize() {
synchronized (lock) {
return size;
}
}
public void consume(int dec) {
public void consume(long dec)
throws ConnectionException {
synchronized (lock) {
log.debug("Consuming by " + dec + " down to " + size);
size -= dec;
log.debug("Consuming by {} down to {}", dec, size);
if (size < 0)
throw new SSHRuntimeException("Window consumed to below 0");
throw new ConnectionException("Window consumed to below 0");
}
}
@@ -70,22 +72,30 @@ 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(long initialWinSize, int maxPacketSize) {
super(initialWinSize, maxPacketSize);
}
public void waitAndConsume(int howMuch)
public long awaitExpansion(long was)
throws ConnectionException {
synchronized (lock) {
while (size < howMuch) {
log.debug("Waiting, need window space for {} bytes", howMuch);
while (size <= was) {
log.debug("Waiting, need size to grow from {} bytes", was);
try {
lock.wait();
} catch (InterruptedException ie) {
throw new ConnectionException(ie);
}
}
consume(howMuch);
return size;
}
}
public void consume(long howMuch) {
try {
super.consume(howMuch);
} catch (ConnectionException e) { // It's a bug if we consume more than remote allowed
throw new SSHRuntimeException(e);
}
}
@@ -95,18 +105,18 @@ public abstract class Window {
public static final class Local
extends Window {
private final int initialSize;
private final int threshold;
private final long initialSize;
private final long threshold;
public Local(int chanID, int initialWinSize, int maxPacketSize) {
super(chanID, "local win", initialWinSize, maxPacketSize);
public Local(long initialWinSize, int maxPacketSize) {
super(initialWinSize, maxPacketSize);
this.initialSize = initialWinSize;
threshold = Math.min(maxPacketSize * 20, initialSize / 4);
}
public int neededAdjustment() {
public long neededAdjustment() {
synchronized (lock) {
return (size - threshold <= 0) ? (initialSize - size) : 0;
return (size <= threshold) ? (initialSize - size) : 0;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,29 +12,10 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.connection.channel.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;
@@ -64,25 +45,35 @@ public abstract class AbstractDirectChannel
public void open()
throws ConnectionException, TransportException {
trans.write(buildOpenReq());
open.await(conn.getTimeout(), TimeUnit.SECONDS);
openEvent.await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
}
private void gotOpenConfirmation(SSHPacket buf) {
init(buf.readInt(), buf.readInt(), buf.readInt());
open.set();
private void gotOpenConfirmation(SSHPacket buf)
throws ConnectionException {
try {
init(buf.readUInt32AsInt(), buf.readUInt32(), buf.readUInt32());
} catch (Buffer.BufferException be) {
throw new ConnectionException(be);
}
openEvent.set();
}
private void gotOpenFailure(SSHPacket buf) {
open.error(new OpenFailException(getType(), buf.readInt(), buf.readString()));
private void gotOpenFailure(SSHPacket buf)
throws ConnectionException {
try {
openEvent.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 2009 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,121 +15,108 @@
*/
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.ConnectionException;
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
import net.schmizz.sshj.transport.TransportException;
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 {
private class DirectTCPIPChannel
extends AbstractDirectChannel {
public static class Parameters {
private final Socket sock;
private final String localHost;
private final int localPort;
private final String remoteHost;
private final int remotePort;
private DirectTCPIPChannel(Connection conn, Socket sock) {
super(conn, "direct-tcpip");
this.sock = sock;
public Parameters(String localHost, int localPort, String remoteHost, int remotePort) {
this.localHost = localHost;
this.localPort = localPort;
this.remoteHost = remoteHost;
this.remotePort = remotePort;
}
private void start()
public String getRemoteHost() {
return remoteHost;
}
public int getRemotePort() {
return remotePort;
}
public String getLocalHost() {
return localHost;
}
public int getLocalPort() {
return localPort;
}
}
public static class DirectTCPIPChannel
extends AbstractDirectChannel {
protected final Socket socket;
protected final Parameters parameters;
public DirectTCPIPChannel(Connection conn, Socket socket, Parameters parameters) {
super(conn, "direct-tcpip");
this.socket = socket;
this.parameters = parameters;
}
protected void start()
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())
socket.setSendBufferSize(getLocalMaxPacketSize());
socket.setReceiveBufferSize(getRemoteMaxPacketSize());
final Event<IOException> soc2chan = new StreamCopier(socket.getInputStream(), getOutputStream())
.bufSize(getRemoteMaxPacketSize())
.errorCallback(closer)
.daemon(true)
.start();
.spawnDaemon("soc2chan");
final Event<IOException> chan2soc = new StreamCopier(getInputStream(), socket.getOutputStream())
.bufSize(getLocalMaxPacketSize())
.spawnDaemon("chan2soc");
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, socket);
}
@Override
protected SSHPacket buildOpenReq() {
return super.buildOpenReq()
.putString(host)
.putInt(port)
.putString(ss.getInetAddress().getHostAddress())
.putInt(ss.getLocalPort());
.putString(parameters.getRemoteHost())
.putUInt32(parameters.getRemotePort())
.putString(parameters.getLocalHost())
.putUInt32(parameters.getLocalPort());
}
}
private final Logger log = LoggerFactory.getLogger(getClass());
private final Logger log = LoggerFactory.getLogger(LocalPortForwarder.class);
private final Connection conn;
private final ServerSocket ss;
private final String host;
private final int port;
private final Parameters parameters;
private final ServerSocket serverSocket;
/**
* Create a local port forwarder with specified binding ({@code listeningAddr}. It does not, however, start
* listening unless {@link #listen() explicitly told to}. The {@link javax.net.ServerSocketFactory#getDefault()
* default} server socket factory is used.
*
* @param conn {@link Connection} implementation
* @param listeningAddr {@link SocketAddress} this forwarder will listen on, if {@code null} then an ephemeral port
* and valid local address will be picked to bind the server socket
* @param host what host the SSH server will further forward to
* @param port port on {@code toHost}
*
* @throws IOException if there is an error binding on specified {@code listeningAddr}
*/
public LocalPortForwarder(Connection conn, SocketAddress listeningAddr, String host, int port)
throws IOException {
this(ServerSocketFactory.getDefault(), conn, listeningAddr, host, port);
}
/**
* Create a local port forwarder with specified binding ({@code listeningAddr}. It does not, however, start
* listening unless {@link #listen() explicitly told to}.
*
* @param ssf factory to use for creating the server socket
* @param conn {@link Connection} implementation
* @param listeningAddr {@link SocketAddress} this forwarder will listen on, if {@code null} then an ephemeral port
* and valid local address will be picked to bind the server socket
* @param host what host the SSH server will further forward to
* @param port port on {@code toHost}
*
* @throws IOException if there is an error binding on specified {@code listeningAddr}
*/
public LocalPortForwarder(ServerSocketFactory ssf, Connection conn, SocketAddress listeningAddr, String host, int port)
throws IOException {
public LocalPortForwarder(Connection conn, Parameters parameters, ServerSocket serverSocket) {
this.conn = conn;
this.host = host;
this.port = port;
this.ss = ssf.createServerSocket();
ss.setReceiveBufferSize(conn.getMaxPacketSize());
ss.bind(listeningAddr);
this.parameters = parameters;
this.serverSocket = serverSocket;
}
/** @return the address to which this forwarder is bound for listening */
public SocketAddress getListeningAddress() {
return ss.getLocalSocketAddress();
protected DirectTCPIPChannel openChannel(Socket socket)
throws TransportException, ConnectionException {
final DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
chan.open();
return chan;
}
/**
@@ -139,15 +126,13 @@ public class LocalPortForwarder {
*/
public void listen()
throws IOException {
log.info("Listening on {}", ss.getLocalSocketAddress());
Socket sock;
log.info("Listening on {}", serverSocket.getLocalSocketAddress());
while (!Thread.currentThread().isInterrupted()) {
sock = ss.accept();
log.info("Got connection from {}", sock.getRemoteSocketAddress());
DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, sock);
chan.open();
chan.start();
final Socket socket = serverSocket.accept();
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
openChannel(socket).start();
}
log.debug("Interrupted!");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 2009 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,7 +19,6 @@ import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.transport.TransportException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
@@ -43,16 +42,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 +70,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.
*
@@ -146,6 +125,7 @@ public interface Session
/** Subsystem API. */
interface Subsystem
extends Channel {
Integer getExitStatus();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,59 +12,41 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.connection.channel.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;
import net.schmizz.sshj.transport.TransportException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
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 {
private final ChannelInputStream err = new ChannelInputStream(this, trans, lwin);
private Integer exitStatus;
private volatile Integer exitStatus;
private Signal exitSignal;
private Boolean wasCoreDumped;
private String exitErrMsg;
private volatile Signal exitSignal;
private volatile Boolean wasCoreDumped;
private volatile String exitErrMsg;
private Boolean canDoFlowControl;
private volatile Boolean canDoFlowControl;
private boolean usedUp;
public SessionChannel(Connection conn) {
super(conn, "session");
@@ -84,12 +66,12 @@ 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);
).await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
}
@Override
@@ -101,31 +83,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 {
log.info("Will request to exec `{}`", command);
checkReuse();
log.debug("Will request to exec `{}`", command);
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command))
.await(conn.getTimeout(), TimeUnit.SECONDS);
.await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
usedUp = true;
return this;
}
@Override
public String getErrorAsString()
throws IOException {
return StreamCopier.copyStreamToString(err);
}
@Override
public InputStream getErrorStream() {
return err;
@@ -146,32 +124,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,15 +154,15 @@ public class
.putBoolean(false)
.putString(authProto)
.putString(authCookie)
.putInt(screen)
).await(conn.getTimeout(), TimeUnit.SECONDS);
.putUInt32(screen)
).await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
}
@Override
public void setEnvVar(String name, String value)
throws ConnectionException, TransportException {
sendChannelRequest("env", true, new Buffer.PlainBuffer().putString(name).putString(value))
.await(conn.getTimeout(), TimeUnit.SECONDS);
.await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
}
@Override
@@ -199,16 +174,20 @@ public class
@Override
public Shell startShell()
throws ConnectionException, TransportException {
sendChannelRequest("shell", true, null).await(conn.getTimeout(), TimeUnit.SECONDS);
checkReuse();
sendChannelRequest("shell", true, null).await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
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);
.await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
usedUp = true;
return this;
}
@@ -230,12 +209,29 @@ 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");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 2009 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,27 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 +42,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 2009 sshj contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,26 +12,6 @@
* 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.
*
* This file may incorporate work covered by the following copyright and
* permission notice:
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.schmizz.sshj.connection.channel.forwarded;
@@ -54,8 +34,9 @@ public abstract class AbstractForwardedChannel
* First 2 args are standard; the others can be parsed from a CHANNEL_OPEN packet.
*/
protected AbstractForwardedChannel(Connection conn, String type, int recipient, int remoteWinSize,
int remoteMaxPacketSize, String origIP, int origPort) {
protected AbstractForwardedChannel(Connection conn, String type,
int recipient, long remoteWinSize, long remoteMaxPacketSize,
String origIP, int origPort) {
super(conn, type);
this.origIP = origIP;
this.origPort = origPort;
@@ -65,20 +46,20 @@ public abstract class AbstractForwardedChannel
@Override
public void confirm()
throws TransportException {
log.info("Confirming `{}` channel #{}", getType(), getID());
log.debug("Confirming `{}` channel #{}", getType(), getID());
// 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()));
open.set();
.putUInt32(getID())
.putUInt32(getLocalWinSize())
.putUInt32(getLocalMaxPacketSize()));
openEvent.set();
}
@Override
public void reject(Reason reason, String message)
throws TransportException {
log.info("Rejecting `{}` channel: {}", getType(), message);
log.debug("Rejecting `{}` channel: {}", getType(), message);
conn.sendOpenFailure(getRecipient(), reason, message);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.schmizz.sshj.connection.channel.forwarded;
import net.schmizz.sshj.common.IOUtils;
@@ -50,7 +49,7 @@ public abstract class AbstractForwardedChannelOpener
new Thread() {
{
setName("ConnectListener");
setName("chanopener");
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 2009 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 2009 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>
@@ -127,7 +127,8 @@ public class RemotePortForwarder
private final Forward fwd;
public ForwardedTCPIPChannel(Connection conn, int recipient, int remoteWinSize, int remoteMaxPacketSize,
public ForwardedTCPIPChannel(Connection conn,
int recipient, long remoteWinSize, long remoteMaxPacketSize,
Forward fwd, String origIP, int origPort) {
super(conn, TYPE, recipient, remoteWinSize, remoteMaxPacketSize, origIP, origPort);
this.fwd = fwd;
@@ -168,7 +169,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 +198,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.getTimeoutMs(), TimeUnit.MILLISECONDS);
}
/** @return the active forwards. */
@@ -211,14 +216,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.readUInt32(), buf.readUInt32(),
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 2009 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
@@ -43,7 +44,7 @@ public class SocketForwardingConnectListener
@Override
public void gotConnect(Channel.Forwarded chan)
throws IOException {
log.info("New connection from " + chan.getOriginatorIP() + ":" + chan.getOriginatorPort());
log.debug("New connection from {}:{}", chan.getOriginatorIP(), chan.getOriginatorPort());
final Socket sock = new Socket();
sock.setSendBufferSize(chan.getLocalMaxPacketSize());
@@ -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 2009 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;
@@ -33,8 +34,9 @@ public class X11Forwarder
public static final String TYPE = "x11";
public X11Channel(Connection conn, int recipient, int remoteWinSize, int remoteMaxPacketSize, String origIP,
int origPort) {
public X11Channel(Connection conn,
int recipient, long remoteWinSize, long remoteMaxPacketSize,
String origIP, int origPort) {
super(conn, TYPE, recipient, remoteWinSize, remoteMaxPacketSize, origIP, origPort);
}
@@ -55,10 +57,13 @@ 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.readUInt32(), buf.readUInt32(),
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 2009 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 2009 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 2009 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 2009 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,9 @@
*/
package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Future;
import net.schmizz.concurrent.Promise;
import net.schmizz.sshj.common.SSHException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,12 +33,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");
}
@@ -54,54 +58,58 @@ public class PacketReader
throws IOException {
readIntoBuffer(lenBuf, 0, lenBuf.length);
return (int) (lenBuf[0] << 24 & 0xff000000L
| lenBuf[1] << 16 & 0x00ff0000L
| lenBuf[2] << 8 & 0x0000ff00L
| lenBuf[3] & 0x000000ffL);
final long len = (lenBuf[0] << 24 & 0xff000000L
| lenBuf[1] << 16 & 0x00ff0000L
| lenBuf[2] << 8 & 0x0000ff00L
| lenBuf[3] & 0x000000ffL);
if (len > SFTPPacket.MAX_SIZE) {
throw new SSHException(String.format("Indicated packet length %d too large", len));
}
return (int) len;
}
public SFTPPacket<Response> readPacket()
throws IOException {
int len = getPacketLength();
packet.rpos(0);
packet.wpos(0);
final int len = getPacketLength();
packet.clear();
packet.ensureCapacity(len);
readIntoBuffer(packet.array(), 0, len);
packet.wpos(len);
return packet;
}
@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());
public Promise<Response, SFTPException> expectResponseTo(long requestId) {
final Promise<Response, SFTPException> promise
= new Promise<Response, SFTPException>("sftp / " + requestId, SFTPException.chainer);
promises.put(requestId, promise);
return promise;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 2009 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 2009 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,44 +19,73 @@ import java.io.IOException;
public class PathHelper {
private final SFTPEngine sftp;
private String dotDir;
public interface Canonicalizer {
String canonicalize(String path)
throws IOException;
public PathHelper(SFTPEngine sftp) {
this.sftp = sftp;
}
public PathComponents getComponents(String path)
public static final String DEFAULT_PATH_SEPARATOR = "/";
private final Canonicalizer canonicalizer;
private final String pathSep;
private String dotDir;
private synchronized String getDotDir() // cached
throws IOException {
if (path.isEmpty() || path.equals("."))
return (dotDir != null) ? dotDir : (dotDir = canonicalizer.canonicalize("."));
}
public PathHelper(Canonicalizer canonicalizer, String pathSep) {
this.canonicalizer = canonicalizer;
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);
}
/**
* Divide the path into {@code PathComponents(parent, name)} while making sure {@code name != "." && name != ".."}
*
* @param path to convert
*
* @return PathComponents
*
* @throws IOException
*/
public PathComponents getComponents(final String path)
throws IOException {
if (path.equals(pathSep))
return getComponents("", "");
if (path.isEmpty() || path.equals(".") || path.equals("." + pathSep))
return getComponents(getDotDir());
final int lastSlash = path.lastIndexOf("/");
final String withoutTrailSep = trimTrailingSeparator(path);
final int lastSep = withoutTrailSep.lastIndexOf(pathSep);
final String parent = (lastSep == -1) ? "" : withoutTrailSep.substring(0, lastSep);
final String name = (lastSep == -1) ? withoutTrailSep : withoutTrailSep.substring(lastSep + pathSep.length());
if (lastSlash == -1)
if (path.equals(".."))
return getComponents(canon(path));
else
return new PathComponents(getDotDir(), path);
final String name = path.substring(lastSlash + 1);
if (name.equals(".") || name.equals(".."))
return getComponents(canon(path));
else {
final String parent = path.substring(0, lastSlash);
return new PathComponents(parent, name);
if (name.equals(".") || name.equals("..")) {
return getComponents(canonicalizer.canonicalize(path));
} else {
return getComponents(parent, name);
}
}
private synchronized String getDotDir()
throws IOException {
return (dotDir != null) ? dotDir : (dotDir = canon("."));
}
private String canon(String path)
throws IOException {
return sftp.canonicalize(path);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 2009 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,11 +20,12 @@ import net.schmizz.sshj.sftp.Response.StatusCode;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class RemoteDirectory
extends RemoteResource {
public RemoteDirectory(Requester requester, String path, String handle) {
public RemoteDirectory(Requester requester, String path, byte[] handle) {
super(requester, path, handle);
}
@@ -32,17 +33,19 @@ public class RemoteDirectory
throws IOException {
List<RemoteResourceInfo> rri = new LinkedList<RemoteResourceInfo>();
loop:
for (; ;) {
Response res = requester.doRequest(newRequest(PacketType.READDIR));
for (; ; ) {
final Response res = requester.request(newRequest(PacketType.READDIR))
.retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS);
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 2009 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,30 +15,28 @@
*/
package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Promise;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.sftp.Response.StatusCode;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
public class RemoteFile
extends RemoteResource {
public RemoteFile(Requester requester, String path, String handle) {
public RemoteFile(Requester requester, String path, byte[] handle) {
super(requester, path, handle);
}
public RemoteFileInputStream getInputStream() {
return new RemoteFileInputStream();
}
public RemoteFileOutputStream getOutputStream() {
return new RemoteFileOutputStream();
}
public FileAttributes fetchAttributes()
throws IOException {
return requester.doRequest(newRequest(PacketType.FSTAT))
return requester.request(newRequest(PacketType.FSTAT))
.retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS)
.ensurePacketTypeIs(PacketType.ATTRS)
.readFileAttributes();
}
@@ -55,10 +53,20 @@ 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));
final Response res = asyncRead(fileOffset, len).retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS);
return checkReadResponse(res, to, offset);
}
protected Promise<Response, SFTPException> asyncRead(long fileOffset, int len)
throws IOException {
return requester.request(newRequest(PacketType.READ).putUInt64(fileOffset).putUInt32(len));
}
protected int checkReadResponse(Response res, byte[] to, int offset)
throws Buffer.BufferException, SFTPException {
switch (res.getType()) {
case DATA:
int recvLen = res.readInt();
int recvLen = res.readUInt32AsInt();
System.arraycopy(res.array(), res.rpos(), to, offset, recvLen);
return recvLen;
@@ -73,42 +81,61 @@ 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)
).ensureStatusPacketIsOK();
checkWriteResponse(asyncWrite(fileOffset, data, off, len));
}
protected Promise<Response, SFTPException> asyncWrite(long fileOffset, byte[] data, int off, int len)
throws IOException {
return requester.request(newRequest(PacketType.WRITE)
.putUInt64(fileOffset)
.putUInt32(len - off)
.putRawBytes(data, off, len)
);
}
private void checkWriteResponse(Promise<Response, SFTPException> responsePromise)
throws SFTPException {
responsePromise.retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS).ensureStatusPacketIsOK();
}
public void setAttributes(FileAttributes attrs)
throws IOException {
requester.doRequest(newRequest(PacketType.FSETSTAT).putFileAttributes(attrs)).ensureStatusPacketIsOK();
requester.request(newRequest(PacketType.FSETSTAT).putFileAttributes(attrs))
.retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS).ensureStatusPacketIsOK();
}
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
extends OutputStream {
private final byte[] b = new byte[1];
private final int maxUnconfirmedWrites;
private final Queue<Promise<Response, SFTPException>> unconfirmedWrites;
private long fileOffset;
public RemoteFileOutputStream() {
this(0);
}
public RemoteFileOutputStream(long fileOffset) {
this.fileOffset = fileOffset;
public RemoteFileOutputStream(long startingOffset) {
this(startingOffset, 0);
}
public RemoteFileOutputStream(long startingOffset, int maxUnconfirmedWrites) {
this.fileOffset = startingOffset;
this.maxUnconfirmedWrites = maxUnconfirmedWrites;
this.unconfirmedWrites = new LinkedList<Promise<Response, SFTPException>>();
}
@Override
@@ -121,10 +148,27 @@ public class RemoteFile
@Override
public void write(byte[] buf, int off, int len)
throws IOException {
RemoteFile.this.write(fileOffset, buf, off, len);
if (unconfirmedWrites.size() > maxUnconfirmedWrites) {
checkWriteResponse(unconfirmedWrites.remove());
}
unconfirmedWrites.add(RemoteFile.this.asyncWrite(fileOffset, buf, off, len));
fileOffset += len;
}
@Override
public void flush()
throws IOException {
while (!unconfirmedWrites.isEmpty()) {
checkWriteResponse(unconfirmedWrites.remove());
}
}
@Override
public void close()
throws IOException {
flush();
}
}
public class RemoteFileInputStream
@@ -140,7 +184,7 @@ public class RemoteFile
this(0);
}
public RemoteFileInputStream(int fileOffset) {
public RemoteFileInputStream(long fileOffset) {
this.fileOffset = fileOffset;
}
@@ -186,4 +230,63 @@ public class RemoteFile
}
}
}
public class ReadAheadRemoteFileInputStream
extends InputStream {
private final byte[] b = new byte[1];
private final int maxUnconfirmedReads;
private final Queue<Promise<Response, SFTPException>> unconfirmedReads;
private long fileOffset;
private boolean eof;
public ReadAheadRemoteFileInputStream(int maxUnconfirmedReads) {
this.maxUnconfirmedReads = maxUnconfirmedReads;
this.unconfirmedReads = new LinkedList<Promise<Response, SFTPException>>();
this.fileOffset = 0;
}
public ReadAheadRemoteFileInputStream(int maxUnconfirmedReads, long fileOffset) {
this.maxUnconfirmedReads = maxUnconfirmedReads;
this.unconfirmedReads = new LinkedList<Promise<Response, SFTPException>>();
this.fileOffset = fileOffset;
}
@Override
public long skip(long n)
throws IOException {
throw new IOException("skip is not supported by ReadAheadFileInputStream, use RemoteFileInputStream instead");
}
@Override
public int read()
throws IOException {
return read(b, 0, 1) == -1 ? -1 : b[0] & 0xff;
}
@Override
public int read(byte[] into, int off, int len)
throws IOException {
while (!eof && unconfirmedReads.size() <= maxUnconfirmedReads) {
// Send read requests as long as there is no EOF and we have not reached the maximum parallelism
unconfirmedReads.add(asyncRead(fileOffset, len));
fileOffset += len;
}
if (unconfirmedReads.isEmpty()) {
assert eof;
return -1;
}
// Retrieve first in
final Response res = unconfirmedReads.remove().retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS);
final int recvLen = checkReadResponse(res, into, off);
if (recvLen == -1) {
eof = true;
}
return recvLen;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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,8 +20,9 @@ import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
abstract class RemoteResource
public abstract class RemoteResource
implements Closeable {
/** Logger */
@@ -29,9 +30,9 @@ abstract class RemoteResource
protected final Requester requester;
protected final String path;
protected final String handle;
protected final byte[] handle;
protected RemoteResource(Requester requester, String path, String handle) {
protected RemoteResource(Requester requester, String path, byte[] handle) {
this.requester = requester;
this.path = path;
this.handle = handle;
@@ -48,8 +49,10 @@ abstract class RemoteResource
@Override
public void close()
throws IOException {
log.info("Closing `{}`", this);
requester.doRequest(newRequest(PacketType.CLOSE)).ensureStatusPacketIsOK();
log.debug("Closing `{}`", this);
requester.request(newRequest(PacketType.CLOSE))
.retrieve(requester.getTimeoutMs(), TimeUnit.MILLISECONDS)
.ensureStatusPacketIsOK();
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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 2009 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 2009 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,17 @@
*/
package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Future;
public class Request
public final class Request
extends SFTPPacket<Request> {
private final PacketType type;
private final long reqID;
private final Future<Response, SFTPException> responseFuture;
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);
putUInt32(reqID);
}
public long getRequestID() {
@@ -40,10 +36,6 @@ public class Request
return type;
}
public Future<Response, SFTPException> getResponseFuture() {
return responseFuture;
}
@Override
public String toString() {
return "Request{" + reqID + ";" + type + "}";

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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,13 +15,19 @@
*/
package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Promise;
import java.io.IOException;
public interface Requester {
PathHelper getPathHelper();
Request newRequest(PacketType type);
Response doRequest(Request req)
Promise<Response, SFTPException> request(Request req)
throws IOException;
int getTimeoutMs();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Shikhar Bhushan
/**
* Copyright 2009 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,7 +17,7 @@ package net.schmizz.sshj.sftp;
import net.schmizz.sshj.common.Buffer;
public class Response
public final class Response
extends SFTPPacket<Response> {
public static enum StatusCode {
@@ -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);
}
}
}

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