mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 07:10:53 +03:00
Compare commits
498 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbe00fabc6 | ||
|
|
ae834134d0 | ||
|
|
bc41908694 | ||
|
|
bf1a855647 | ||
|
|
cd3b0a5bd6 | ||
|
|
b01eccda4a | ||
|
|
4c9ebc306d | ||
|
|
c3f75cda19 | ||
|
|
1f0e2b1e69 | ||
|
|
8e55e50fd9 | ||
|
|
eb8b7b51ca | ||
|
|
a2cccd5cef | ||
|
|
50403483da | ||
|
|
3c230a0fc4 | ||
|
|
4f152749ce | ||
|
|
e0df6a5fb5 | ||
|
|
f36c011844 | ||
|
|
94113eb6f5 | ||
|
|
0532f27a78 | ||
|
|
bd67135ffa | ||
|
|
ca49ca324f | ||
|
|
ac2ffbc367 | ||
|
|
dbb0eb0238 | ||
|
|
347e6ad655 | ||
|
|
2622833831 | ||
|
|
c0487c9ee5 | ||
|
|
3372db75b5 | ||
|
|
db75bad25c | ||
|
|
a73776ad40 | ||
|
|
237c7d18b6 | ||
|
|
b7c8cda851 | ||
|
|
2b6fedc939 | ||
|
|
51e1ff24e4 | ||
|
|
05efcb4889 | ||
|
|
d456612d25 | ||
|
|
6feed72251 | ||
|
|
67e44241d0 | ||
|
|
a2a5923767 | ||
|
|
bdf9ab7452 | ||
|
|
afdfa91eb7 | ||
|
|
29a6cf6f79 | ||
|
|
eece80cf48 | ||
|
|
7973cb1ff6 | ||
|
|
75c0ae9a83 | ||
|
|
f2314e74ed | ||
|
|
e041e3e1e3 | ||
|
|
47df71c836 | ||
|
|
e24ed6ee7b | ||
|
|
10f8645ecd | ||
|
|
d520585a09 | ||
|
|
28a11b0b45 | ||
|
|
a335185827 | ||
|
|
74a4012023 | ||
|
|
c98ad22a7a | ||
|
|
1c749da957 | ||
|
|
5d81e87bce | ||
|
|
d18e9d9961 | ||
|
|
84990ada08 | ||
|
|
9c424f9431 | ||
|
|
dec00efcaa | ||
|
|
742553912c | ||
|
|
e81fdb8d8b | ||
|
|
782ff9b83e | ||
|
|
84d15f4cf5 | ||
|
|
1ebcbb07ba | ||
|
|
9982e5c30e | ||
|
|
3f340d6927 | ||
|
|
b8eec64a37 | ||
|
|
314d9d01cf | ||
|
|
c526f8e3de | ||
|
|
9529c30105 | ||
|
|
6a476858d1 | ||
|
|
6bfb268c11 | ||
|
|
e334525da5 | ||
|
|
8776500fa0 | ||
|
|
a747db88ed | ||
|
|
97065264de | ||
|
|
7c26ac669a | ||
|
|
1c5b462206 | ||
|
|
4cb9610cdd | ||
|
|
b9d0a03cb3 | ||
|
|
4adc83b9df | ||
|
|
14edb33fa9 | ||
|
|
8e74330b0b | ||
|
|
5217d34198 | ||
|
|
d3d019c1c2 | ||
|
|
49185b044d | ||
|
|
a18d623f44 | ||
|
|
6855873ffd | ||
|
|
2ca8d8b19e | ||
|
|
da32b145df | ||
|
|
8ea6bb4a66 | ||
|
|
6cf767528a | ||
|
|
b123a6ae30 | ||
|
|
4250c61e45 | ||
|
|
ace09fa8c8 | ||
|
|
8398b6e3c3 | ||
|
|
3c1e0c1629 | ||
|
|
e6c7c17664 | ||
|
|
1e061aef25 | ||
|
|
66b772bac1 | ||
|
|
fc535a5e76 | ||
|
|
c7373f05cc | ||
|
|
3ebd2eb363 | ||
|
|
8638091517 | ||
|
|
5fc08a3fc8 | ||
|
|
92df7c6924 | ||
|
|
8c0967ca93 | ||
|
|
cb5b7f0943 | ||
|
|
de2ede05e7 | ||
|
|
4a90f99c5f | ||
|
|
e348f698e6 | ||
|
|
d59efaa5f9 | ||
|
|
83c5f2f815 | ||
|
|
b17d3fe867 | ||
|
|
3cefda5bd3 | ||
|
|
18f364a283 | ||
|
|
d68032a9b8 | ||
|
|
c73ba8bfa7 | ||
|
|
7bbfd40627 | ||
|
|
7ae84be548 | ||
|
|
f2793d1acf | ||
|
|
bca5883422 | ||
|
|
3e54e2c955 | ||
|
|
a7802ddcde | ||
|
|
a7872b394b | ||
|
|
3ade3977ef | ||
|
|
efb2c547f9 | ||
|
|
703a0df09d | ||
|
|
73de5b7b08 | ||
|
|
665cbf078a | ||
|
|
b3ea908996 | ||
|
|
11da49a4e7 | ||
|
|
5b1f9f2a7d | ||
|
|
268de458e3 | ||
|
|
834f0f22cd | ||
|
|
1daf456cbe | ||
|
|
ea11d34ac8 | ||
|
|
e961dc1b27 | ||
|
|
25fbff245f | ||
|
|
333c23e167 | ||
|
|
cf32842d0d | ||
|
|
70720de71b | ||
|
|
44e1ce1358 | ||
|
|
921f41f9de | ||
|
|
34c4be848a | ||
|
|
d10e303b1a | ||
|
|
46791c87f5 | ||
|
|
af0d873e5b | ||
|
|
d37b54b1fd | ||
|
|
c4408ac6dd | ||
|
|
ebbf440304 | ||
|
|
ef5a54d33f | ||
|
|
66514836c8 | ||
|
|
e943d80049 | ||
|
|
81931f3b7a | ||
|
|
b8bfc19ecf | ||
|
|
0cb62c6d44 | ||
|
|
0ccc57b5af | ||
|
|
4806b1d6c7 | ||
|
|
ecc1d06dc2 | ||
|
|
d95586508d | ||
|
|
5ee2f0a417 | ||
|
|
2a7278d239 | ||
|
|
0875417dde | ||
|
|
0a3ad4f68f | ||
|
|
fe58ecdee5 | ||
|
|
264e10b40c | ||
|
|
a00015969b | ||
|
|
d6c22fef55 | ||
|
|
9886facf42 | ||
|
|
01be48508d | ||
|
|
bdc541c959 | ||
|
|
f2ebbe288f | ||
|
|
9297338195 | ||
|
|
a8d2ea2028 | ||
|
|
f34667521d | ||
|
|
77f5d7fdb8 | ||
|
|
08d0e59b6b | ||
|
|
5c540b6889 | ||
|
|
baa8c8e995 | ||
|
|
f354fd6661 | ||
|
|
93f1543af8 | ||
|
|
63424657da | ||
|
|
131e85c4d0 | ||
|
|
587684c6a8 | ||
|
|
66f67db21b | ||
|
|
3356f533d0 | ||
|
|
97535bbcae | ||
|
|
896b0ea288 | ||
|
|
60d54fa5de | ||
|
|
06e421e752 | ||
|
|
b5796f5e74 | ||
|
|
0f7355a277 | ||
|
|
466ff99e1c | ||
|
|
1f992c3fae | ||
|
|
df6019accc | ||
|
|
fdb891b842 | ||
|
|
5159a799df | ||
|
|
78e5a2e30e | ||
|
|
db22f08f97 | ||
|
|
c8cfc796af | ||
|
|
d9c0c6725c | ||
|
|
b2297c6b44 | ||
|
|
e10ad28f2f | ||
|
|
61fc00a90a | ||
|
|
c8ef7ff0ca | ||
|
|
e6c4f6ae69 | ||
|
|
3418df7a56 | ||
|
|
0ddd1f38c5 | ||
|
|
0ec6918d7a | ||
|
|
88a88c5dba | ||
|
|
6656214803 | ||
|
|
c781724028 | ||
|
|
eefaa26882 | ||
|
|
0d52441f01 | ||
|
|
9539ff6b7a | ||
|
|
1ced1d4fdc | ||
|
|
77924fd0be | ||
|
|
3f195649fa | ||
|
|
42a4358f5c | ||
|
|
61ce0f4868 | ||
|
|
777995af3b | ||
|
|
635cf88acd | ||
|
|
ce515fddcd | ||
|
|
9acff6202c | ||
|
|
cbd118e0b1 | ||
|
|
a8cf749d95 | ||
|
|
f3d4707ef0 | ||
|
|
4c5da634ad | ||
|
|
2fdafb76fd | ||
|
|
80b164a299 | ||
|
|
75418f33b7 | ||
|
|
732de2b605 | ||
|
|
4fb56b868f | ||
|
|
a877ec1448 | ||
|
|
b44631ea97 | ||
|
|
a50962ba2f | ||
|
|
e8215e4af2 | ||
|
|
3c2bda3196 | ||
|
|
b13e22084b | ||
|
|
e7ba0e1e26 | ||
|
|
f712720538 | ||
|
|
540708e540 | ||
|
|
e4d3a1f866 | ||
|
|
33969340e2 | ||
|
|
d65df3c9bc | ||
|
|
d2b9248535 | ||
|
|
431be8e7c7 | ||
|
|
885c602ab8 | ||
|
|
8262e8fc98 | ||
|
|
844c5d7f77 | ||
|
|
fb690c4fb0 | ||
|
|
ab04596a20 | ||
|
|
9ffdc35f93 | ||
|
|
93e23f4cfb | ||
|
|
504637099d | ||
|
|
cafd9217bf | ||
|
|
c627fabebd | ||
|
|
1c4781a65d | ||
|
|
aac7af2827 | ||
|
|
11c286b9b9 | ||
|
|
7fae513fd8 | ||
|
|
53ad9d2288 | ||
|
|
ee07072846 | ||
|
|
d38bbbcdf7 | ||
|
|
bc59c81dbc | ||
|
|
d70d37cf4e | ||
|
|
777d82912c | ||
|
|
f5db3e1563 | ||
|
|
7e524f5c6f | ||
|
|
dbb3f62e82 | ||
|
|
16a363fef6 | ||
|
|
9b0d39a798 | ||
|
|
81e36153d7 | ||
|
|
3026be282a | ||
|
|
8eedeb25fa | ||
|
|
de11880648 | ||
|
|
1ff4772f3f | ||
|
|
22a5ffe735 | ||
|
|
7a77f85ced | ||
|
|
0002fe8b40 | ||
|
|
3028e7f218 | ||
|
|
333e1cb7b8 | ||
|
|
945d430916 | ||
|
|
73b903784a | ||
|
|
7d53649a85 | ||
|
|
e193db9a14 | ||
|
|
a942edb911 | ||
|
|
137a7f5956 | ||
|
|
718ff503df | ||
|
|
d933b2538e | ||
|
|
ea6f9ceed2 | ||
|
|
07c61b14e8 | ||
|
|
4b175e6938 | ||
|
|
f7e47cffa0 | ||
|
|
42dddc7f7e | ||
|
|
f1b3dbb102 | ||
|
|
f83bf2cd3f | ||
|
|
be11cbb848 | ||
|
|
43b0599e1f | ||
|
|
b218186cae | ||
|
|
184236c3d5 | ||
|
|
cb1d773659 | ||
|
|
378665cb46 | ||
|
|
a5272dc413 | ||
|
|
60552fd001 | ||
|
|
ef082c668a | ||
|
|
e66386eb1c | ||
|
|
0937ec9800 | ||
|
|
4b2f42804e | ||
|
|
01765d24d2 | ||
|
|
1a2351c5ee | ||
|
|
1cec011401 | ||
|
|
52338c13cb | ||
|
|
09cf21f61a | ||
|
|
04c2e7b6b8 | ||
|
|
822f196dd8 | ||
|
|
a88a574b10 | ||
|
|
5cd6986355 | ||
|
|
b5d206bbcb | ||
|
|
4eae26c551 | ||
|
|
b950f88f52 | ||
|
|
3267860db4 | ||
|
|
d6eb5a040e | ||
|
|
21da5b9f65 | ||
|
|
6b66a952d4 | ||
|
|
aa4faf3f25 | ||
|
|
4be02450dd | ||
|
|
0cec27c28e | ||
|
|
4384367a1b | ||
|
|
4549648a76 | ||
|
|
20e2161022 | ||
|
|
fb0f3afa17 | ||
|
|
114c2bb424 | ||
|
|
079bde5dbf | ||
|
|
eaee42b017 | ||
|
|
8b61d96808 | ||
|
|
73fcc81e83 | ||
|
|
0f7926d4fa | ||
|
|
ca6f15650a | ||
|
|
eb78dc499d | ||
|
|
a852f33a15 | ||
|
|
ccabc1a20c | ||
|
|
cb2986d32e | ||
|
|
dc70f08e45 | ||
|
|
bf68ec18b2 | ||
|
|
7e78260ca9 | ||
|
|
27c60cee60 | ||
|
|
551b8b4fcf | ||
|
|
fd591e70be | ||
|
|
d177b239c6 | ||
|
|
adf44e2dc0 | ||
|
|
7810b5f653 | ||
|
|
3695e2a184 | ||
|
|
17d8e91f05 | ||
|
|
3c3715eccf | ||
|
|
2ff9f2ae50 | ||
|
|
4f7b29da0d | ||
|
|
2d49cb4d77 | ||
|
|
d752bc36ff | ||
|
|
99e24b7323 | ||
|
|
40b401406c | ||
|
|
803b154505 | ||
|
|
ff5935af2a | ||
|
|
430ebe27ea | ||
|
|
a0109dd8fa | ||
|
|
85abcb7aad | ||
|
|
4de741359e | ||
|
|
ab705d7f2a | ||
|
|
f89c0cc2f0 | ||
|
|
d8cc271cd3 | ||
|
|
d1043ea288 | ||
|
|
ce930c969b | ||
|
|
a2c82de260 | ||
|
|
2e70b56ba3 | ||
|
|
9761f44cd4 | ||
|
|
137dc5ed42 | ||
|
|
286a22270b | ||
|
|
aa9f4e192f | ||
|
|
41ac277023 | ||
|
|
c56f9997f4 | ||
|
|
b92dece6ec | ||
|
|
2880fe2bc0 | ||
|
|
ce5fad9809 | ||
|
|
38883bf15d | ||
|
|
20c5ab8dfc | ||
|
|
d9c438ed16 | ||
|
|
653e8ad4f2 | ||
|
|
c46dc913e8 | ||
|
|
069ebbd47d | ||
|
|
da2cec8fa2 | ||
|
|
75caa8bcf3 | ||
|
|
f664b7b24f | ||
|
|
70f3aeee68 | ||
|
|
882d40a1b6 | ||
|
|
9649b2f72e | ||
|
|
79a8d0b3ad | ||
|
|
2e7fcfd308 | ||
|
|
946422112d | ||
|
|
b11f0be894 | ||
|
|
ba6e5292c8 | ||
|
|
c8de9ed915 | ||
|
|
7ccd078e52 | ||
|
|
0aa8d5e141 | ||
|
|
2e32bb9aca | ||
|
|
2f4fa62b14 | ||
|
|
8a4367cc7a | ||
|
|
168272ad3b | ||
|
|
17eb5cff0f | ||
|
|
ebd5036d64 | ||
|
|
7797d774ac | ||
|
|
888a8f60d7 | ||
|
|
974e88efb4 | ||
|
|
9a4a24737f | ||
|
|
a1d17982ae | ||
|
|
3beee8350d | ||
|
|
3cd446b462 | ||
|
|
486dbf2b05 | ||
|
|
3cb235bbfd | ||
|
|
2882129211 | ||
|
|
fb97ccb67c | ||
|
|
8b21eff1d2 | ||
|
|
7874e7dbfd | ||
|
|
efc7702195 | ||
|
|
34a7b8e065 | ||
|
|
50c42b97a3 | ||
|
|
826660ab3f | ||
|
|
a3b6fde44a | ||
|
|
69555e9c74 | ||
|
|
241f61bdd1 | ||
|
|
0051dd420c | ||
|
|
f2abc4b397 | ||
|
|
fe0d42fc97 | ||
|
|
19e4670c24 | ||
|
|
fbd6e00720 | ||
|
|
f69cdb1505 | ||
|
|
135b1c819b | ||
|
|
9c51b862cd | ||
|
|
a6353cbb2d | ||
|
|
f11055a726 | ||
|
|
da98153ab6 | ||
|
|
70d4dbd88a | ||
|
|
b2a74935d4 | ||
|
|
f3c072fb06 | ||
|
|
738b317dcf | ||
|
|
d87e0c2da2 | ||
|
|
72ff74c99d | ||
|
|
5cb4f1be43 | ||
|
|
693379feb4 | ||
|
|
98529f733c | ||
|
|
582f789db4 | ||
|
|
c16ccc2644 | ||
|
|
5c3ba2282c | ||
|
|
e765536fa1 | ||
|
|
068fbdfc55 | ||
|
|
f4f9249b35 | ||
|
|
252fcebeac | ||
|
|
a7ccccaacf | ||
|
|
2420027a58 | ||
|
|
b54a4abfce | ||
|
|
0c28deab88 | ||
|
|
12c79dc2b2 | ||
|
|
6ef3dacc81 | ||
|
|
700998d064 | ||
|
|
dea6e3db91 | ||
|
|
dc5ff451f0 | ||
|
|
ca521500ed | ||
|
|
ca07e0d25a | ||
|
|
f372ae782a | ||
|
|
460a730879 | ||
|
|
25a18c6fd7 | ||
|
|
53389e6989 | ||
|
|
2f1b2c4e77 | ||
|
|
09b93ec11a | ||
|
|
63b321e8d1 | ||
|
|
49e3d4ec27 | ||
|
|
8e9a773e92 | ||
|
|
f30afe50f4 | ||
|
|
4af8fab434 | ||
|
|
436e5774a8 | ||
|
|
7a2850d760 | ||
|
|
53d8d548cf | ||
|
|
89d03361ba | ||
|
|
db4185608d | ||
|
|
71b6cee424 | ||
|
|
bdb1fe73e3 | ||
|
|
e327e2e8ad | ||
|
|
66fd4a4823 | ||
|
|
8be0e1c8b0 | ||
|
|
37de8faf9b | ||
|
|
9e92463ff2 | ||
|
|
9cbb75084a | ||
|
|
54ee1a6917 | ||
|
|
dd1be04a61 | ||
|
|
839021cd2b | ||
|
|
e6f2ed3035 | ||
|
|
6581772c18 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.bat text eol=crlf
|
||||
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# IntelliJ IDEA
|
||||
.idea/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# Eclipe
|
||||
.project
|
||||
.classpath
|
||||
.settings/
|
||||
|
||||
# Output dirs
|
||||
target/
|
||||
build/
|
||||
.gradle/
|
||||
|
||||
|
||||
2
.travis.yml
Normal file
2
.travis.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
language: java
|
||||
sudo: false
|
||||
1
CONTRIBUTORS
Normal file
1
CONTRIBUTORS
Normal 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-
|
||||
13
LICENSE_HEADER
Normal file
13
LICENSE_HEADER
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright (C)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.
|
||||
59
NOTICE
59
NOTICE
@@ -1,8 +1,65 @@
|
||||
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/):
|
||||
|
||||
- Apache MINA SSHD
|
||||
- Apache Commons-Net
|
||||
|
||||
|
||||
// Apache Mina SSHD notice
|
||||
|
||||
=========================================================================
|
||||
== NOTICE file for use with the Apache License, Version 2.0, ==
|
||||
== in this case for the SSHD distribution. ==
|
||||
=========================================================================
|
||||
|
||||
This product contains software developped by JCraft,Inc. and subject to
|
||||
the following license:
|
||||
|
||||
Copyright (c) 2002,2003,2004,2005,2006,2007,2008 Atsuhiko Yamanaka, JCraft,Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
--------------------------------------------------------------------------------
|
||||
Copyright (c) 2000 - 2006 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
142
README.adoc
Normal file
142
README.adoc
Normal file
@@ -0,0 +1,142 @@
|
||||
= sshj - SSHv2 library for Java
|
||||
Jeroen van Erp
|
||||
:sshj_groupid: com.hierynomus
|
||||
:sshj_version: 0.15.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,ctr}`, `3des-{cbc,ctr}`, `twofish{128,192,256}-{cbc,ctr}`, `twofish-cbc`, `serpent{128,192,256}-{cbc,ctr}`, `idea-{cbc,ctr}`, `cast128-{cbc,ctr}`, `arcfour`, `arcfour{128,256}`
|
||||
SSHJ also supports the following extended (non official) ciphers: `camellia{128,192,256}-{cbc,ctr}`, `camellia{128,192,256}-{cbc,ctr}@openssh.org`
|
||||
|
||||
key exchange::
|
||||
`diffie-hellman-group1-sha1`, `diffie-hellman-group14-sha1`, `diffie-hellman-group-exchange-sha1`, `diffie-hellman-group-exchange-sha256`,
|
||||
`ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `curve25519-sha256@libssh.org`
|
||||
|
||||
signatures::
|
||||
`ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ssh-ed25519`
|
||||
|
||||
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!)
|
||||
|
||||
== Comparing to other implementations
|
||||
http://ssh-comparison.quendi.de/comparison.html[SSH Implementation Comparison]
|
||||
|
||||
== 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.16.0 (2016-??-??)::
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/239[#239]: Remote port forwards did not work if you used the empty string as address, or a catch-all address.
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/242[#242]: Added OSGI headers to sources jar manifest
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/236[#236]: Remote Port forwarding with dynamic port allocation fails with BufferUnderflowException
|
||||
* Upgraded gradle distribution to 2.12
|
||||
* Closed https://github.com/hierynomus/sshj/issues/234[#234]: Dropped Java6 support (0.15.0 was already Java6 incompatible due to Java7 dependency)
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/118[#118]: Added configuration switch for waiting on a server ident before sending the client ident.
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/114[#114]: Added javadoc that you always need to call close() on a Command before inspecting the exit codes.
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/237[#237]: Fixed race condition if a `hostkeys-00@openssh.com` global request is received directly after a successful auth.
|
||||
SSHJ 0.15.0 (2015-11-20)::
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/220[#220]: Added support for `ssh-ed25519` host keys
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/225[#225]: Fixed bug in ECDSA fingerprint calculation that sometimes produced an incorrect fingerprint
|
||||
* Added `arcfour` Stream Ciphers from RFC4253 and RFC4345
|
||||
* Added all Block Ciphers from RFC4344 and RFC4253
|
||||
SSHJ 0.14.0 (2015-11-04)::
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/171[#171]: Added support for `curve25519-sha256@libssh.org` key exchange algorithm
|
||||
* Added support for `ecdh-sha2-nistp256`, `ecdh-sha2-nistp384` and `ecdh-sha2-nistp521` key exchange algorithms
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/167[#167]: Added support for `diffie-hellman-group-exchange-sha1` and `diffie-hellman-group-exchange-sha256` key exchange methods
|
||||
* Fixed https://github.com/hierynomus/sshj/issues/212[#212]: Configure path escaping to enable shell expansion to work correctly
|
||||
* Merged https://github.com/hierynomus/sshj/issues/210[#210]: RemoteFileInputStream.skip returns wrong value (Fixes https://github.com/hierynomus/sshj/issues/209[#209])
|
||||
* Merged https://github.com/hierynomus/sshj/issues/208[#208]: Added SCP bandwidth limitation support
|
||||
* Merged https://github.com/hierynomus/sshj/issues/211[#211]: Made keyfile format detection more robust
|
||||
SSHJ 0.13.0 (2015-08-18)::
|
||||
* Merged https://github.com/hierynomus/sshj/issues/199[#199]: Fix for IndexOutOfBoundsException in ReadAheadRemoteFileInputStream, fixes https://github.com/hierynomus/sshj/issues/183[#183]
|
||||
* Merged https://github.com/hierynomus/sshj/issues/195[#195]: New authentication supported: `gssapi-with-mic`
|
||||
* Merged https://github.com/hierynomus/sshj/issues/201[#201]: New option to verify negotiated key exchange algorithms
|
||||
* Merged https://github.com/hierynomus/sshj/issues/196[#196]: Fix for looking up complete hostname in known hosts file
|
||||
SSHJ 0.12.0 (2015-04-14)::
|
||||
* Added support for HTTP proxies when running JDK6 or JDK7, fixes: https://github.com/hierynomus/sshj/issues/170[#170]
|
||||
* Merged https://github.com/hierynomus/sshj/issues/186[#186]: Fix for detecting end-of-stream
|
||||
* Compiling to JDK6, fixes https://github.com/hierynomus/sshj/issues/179[#179] and https://github.com/hierynomus/sshj/issues/185[#185]
|
||||
* Correctly close socket and channel when LocalPortForwarder fails to open and start the channel (Fixes https://github.com/hierynomus/sshj/issues/175[#175] and https://github.com/hierynomus/sshj/issues/176[#176])
|
||||
* Merged https://github.com/hierynomus/sshj/issues/181[#181]: Invalid write packet length when reading with offset (Fixes https://github.com/hierynomus/sshj/issues/180[#180])
|
||||
SSHJ 0.11.0 (2015-01-23)::
|
||||
* New maven coordinates `com.hierynomus:sshj:0.11.0` as https://github.com/hierynomus[@hierynomus] took over as maintainer of SSHJ
|
||||
* Migrated build system to Gradle 2.2.1
|
||||
* Merged https://github.com/hierynomus/sshj/issues/150[#150]: Fix for incorrect file handle on some SSH servers, fixes: https://github.com/hierynomus/sshj/issues/54[#54], https://github.com/hierynomus/sshj/issues/119[#119], https://github.com/hierynomus/sshj/issues/168[#168], https://github.com/hierynomus/sshj/issues/169[#169]
|
||||
* 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]
|
||||
64
README.rst
64
README.rst
@@ -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
177
build-publishing.gradle
Normal 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
|
||||
}
|
||||
198
build.gradle
Normal file
198
build.gradle
Normal file
@@ -0,0 +1,198 @@
|
||||
plugins {
|
||||
id "java"
|
||||
id "maven"
|
||||
id "idea"
|
||||
id "signing"
|
||||
id "osgi"
|
||||
id "org.ajoberstar.release-opinion" version "1.4.0-rc.1"
|
||||
id "com.github.hierynomus.license" version "0.12.1"
|
||||
}
|
||||
|
||||
group = "com.hierynomus"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.7
|
||||
targetCompatibility = 1.7
|
||||
|
||||
configurations.compile.transitive = false
|
||||
|
||||
idea {
|
||||
module {
|
||||
downloadJavadoc = true
|
||||
downloadSources = true
|
||||
}
|
||||
}
|
||||
|
||||
license {
|
||||
mapping {
|
||||
java = 'SLASHSTAR_STYLE'
|
||||
}
|
||||
header rootProject.file('LICENSE_HEADER')
|
||||
strictCheck true
|
||||
}
|
||||
|
||||
release {
|
||||
grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir)
|
||||
}
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
include "**/*Test.*"
|
||||
if (!project.hasProperty("allTests")) {
|
||||
useJUnit {
|
||||
excludeCategories 'com.hierynomus.sshj.test.SlowTests'
|
||||
excludeCategories 'com.hierynomus.sshj.test.KnownFailingTests'
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
compile "net.vrallev.ecc:ecc-25519-java:1.0.1"
|
||||
|
||||
testCompile "junit:junit:4.11"
|
||||
testCompile "org.mockito:mockito-core:1.9.5"
|
||||
testCompile "org.apache.sshd:sshd-core:1.1.0"
|
||||
testRuntime "ch.qos.logback:logback-classic:1.1.2"
|
||||
testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17'
|
||||
testCompile 'org.apache.httpcomponents:httpclient:4.5.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", "net.i2p*"
|
||||
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
|
||||
manifest = project.tasks.jar.manifest
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives javadocJar, sourcesJar
|
||||
}
|
||||
|
||||
signing {
|
||||
required { !version.toString().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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.release.dependsOn 'build', 'uploadArchives'
|
||||
101
examples/pom.xml
Normal file
101
examples/pom.xml
Normal 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>com.hierynomus</groupId>
|
||||
<artifactId>sshj-examples</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.14.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>com.hierynomus</groupId>
|
||||
<artifactId>sshj</artifactId>
|
||||
<version>0.15.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>
|
||||
36
examples/src/main/java/net/schmizz/sshj/examples/Exec.java
Normal file
36
examples/src/main/java/net/schmizz/sshj/examples/Exec.java
Normal file
@@ -0,0 +1,36 @@
|
||||
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 {
|
||||
final SSHClient ssh = new SSHClient();
|
||||
ssh.loadKnownHosts();
|
||||
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
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
|
||||
* all incoming connections to SSH server which further forwards them to a specified address and port.
|
||||
*/
|
||||
public class LocalPF {
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
|
||||
ssh.loadKnownHosts();
|
||||
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
|
||||
/*
|
||||
* _We_ listen on localhost:8080 and forward all connections on to server, which then forwards it to
|
||||
* google.com:80
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +1,4 @@
|
||||
/*
|
||||
* 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 examples;
|
||||
package net.schmizz.sshj.examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder.Forward;
|
||||
@@ -28,10 +13,6 @@ import java.net.InetSocketAddress;
|
||||
*/
|
||||
public class RemotePF {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException {
|
||||
SSHClient client = new SSHClient();
|
||||
@@ -50,11 +31,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();
|
||||
@@ -0,0 +1,60 @@
|
||||
package net.schmizz.sshj.examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.common.StreamCopier;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session.Shell;
|
||||
import net.schmizz.sshj.transport.verification.ConsoleKnownHostsVerifier;
|
||||
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/** A very rudimentary psuedo-terminal based on console I/O. */
|
||||
class RudimentaryPTY {
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException {
|
||||
|
||||
final SSHClient ssh = new SSHClient();
|
||||
|
||||
final File khFile = new File(OpenSSHKnownHosts.detectSSHDir(), "known_hosts");
|
||||
ssh.addHostKeyVerifier(new ConsoleKnownHostsVerifier(khFile, System.console()));
|
||||
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
|
||||
final Session session = ssh.startSession();
|
||||
try {
|
||||
|
||||
session.allocateDefaultPTY();
|
||||
|
||||
final Shell shell = session.startShell();
|
||||
|
||||
new StreamCopier(shell.getInputStream(), System.out)
|
||||
.bufSize(shell.getLocalMaxPacketSize())
|
||||
.spawn("stdout");
|
||||
|
||||
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 {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package net.schmizz.sshj.examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.xfer.FileSystemFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** This example demonstrates downloading of a file over SCP from the SSH server. */
|
||||
public class SCPDownload {
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
// ssh.useCompression(); // Can lead to significant speedup (needs JZlib in classpath)
|
||||
ssh.loadKnownHosts();
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
ssh.newSCPFileTransfer().download("test_file", new FileSystemFile("/tmp/"));
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.schmizz.sshj.examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.xfer.FileSystemFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/** This example demonstrates uploading of a file over SCP to the SSH server. */
|
||||
public class SCPUpload {
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException, ClassNotFoundException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
ssh.loadKnownHosts();
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
|
||||
// Present here to demo algorithm renegotiation - could have just put this before connect()
|
||||
// Make sure JZlib is in classpath for this to work
|
||||
ssh.useCompression();
|
||||
|
||||
final String src = System.getProperty("user.home") + File.separator + "test_file";
|
||||
ssh.newSCPFileTransfer().upload(new FileSystemFile(src), "/tmp/");
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
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;
|
||||
|
||||
/** This example demonstrates downloading of a file over SFTP from the SSH server. */
|
||||
public class SFTPDownload {
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException {
|
||||
final SSHClient ssh = new SSHClient();
|
||||
ssh.loadKnownHosts();
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
final SFTPClient sftp = ssh.newSFTPClient();
|
||||
try {
|
||||
sftp.get("test_file", new FileSystemFile("/tmp"));
|
||||
} finally {
|
||||
sftp.close();
|
||||
}
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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;
|
||||
|
||||
/** This example demonstrates uploading of a file over SFTP to the SSH server. */
|
||||
public class SFTPUpload {
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException {
|
||||
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 SFTPClient sftp = ssh.newSFTPClient();
|
||||
try {
|
||||
sftp.put(new FileSystemFile(src), "/tmp");
|
||||
} finally {
|
||||
sftp.close();
|
||||
}
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +1,4 @@
|
||||
/*
|
||||
* 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 examples;
|
||||
package net.schmizz.sshj.examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.common.StreamCopier;
|
||||
@@ -27,13 +12,9 @@ import java.net.InetSocketAddress;
|
||||
/** This example demonstrates how forwarding X11 connections from a remote host can be accomplished. */
|
||||
public class X11 {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
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();
|
||||
@@ -59,10 +40,10 @@ public class X11 {
|
||||
*/
|
||||
sess.reqX11Forwarding("MIT-MAGIC-COOKIE-1", "b0956167c9ad8f34c8a2788878307dc9", 0);
|
||||
|
||||
Command cmd = sess.exec("mate");
|
||||
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();
|
||||
@@ -71,4 +52,4 @@ public class X11 {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Fri Mar 18 11:26:35 CET 2016
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip
|
||||
164
gradlew
vendored
Executable file
164
gradlew
vendored
Executable 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
90
gradlew.bat
vendored
Normal 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
255
pom.xml
@@ -1,255 +0,0 @@
|
||||
<?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">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>net.schmizz</groupId>
|
||||
<artifactId>sshj</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.1.1</version>
|
||||
|
||||
<name>sshj</name>
|
||||
<description>SSHv2 library for Java</description>
|
||||
<url>http://github.com/shikhar/sshj</url>
|
||||
|
||||
<inceptionYear>2009</inceptionYear>
|
||||
|
||||
<issueManagement>
|
||||
<system>github</system>
|
||||
<url>http://github.com/shikhar/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>
|
||||
</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>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.5.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15</artifactId>
|
||||
<version>1.45</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jzlib</artifactId>
|
||||
<version>1.0.7</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-core</artifactId>
|
||||
<version>0.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.15</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jdmk</groupId>
|
||||
<artifactId>jmxtools</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jmx</groupId>
|
||||
<artifactId>jmxri</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.jms</groupId>
|
||||
<artifactId>jms</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>mail</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>oro</groupId>
|
||||
<artifactId>oro</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>shikhar</id>
|
||||
<name>Shikhar Bhushan</name>
|
||||
<email>shikhar@schmizz.net</email>
|
||||
<url>http://schmizz.net</url>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>examples/*.java</exclude>
|
||||
</excludes>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<configuration>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>sonatype-nexus-staging</id>
|
||||
<name>Nexus Release Repository</name>
|
||||
<url>http://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>sonatype-nexus-snapshots</id>
|
||||
<name>Sonatype Nexus Snapshots</name>
|
||||
<url>http://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>dev</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15</artifactId>
|
||||
<version>1.45</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jzlib</artifactId>
|
||||
<version>1.0.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.5.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.15</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jdmk</groupId>
|
||||
<artifactId>jmxtools</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jmx</groupId>
|
||||
<artifactId>jmxri</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.jms</groupId>
|
||||
<artifactId>jms</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>mail</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>oro</groupId>
|
||||
<artifactId>oro</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>release-sign-artifacts</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>performRelease</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.0</version>
|
||||
<configuration>
|
||||
<passphrase>${gpg.passphrase}</passphrase>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = "sshj"
|
||||
13
src/etc/license-header
Normal file
13
src/etc/license-header
Normal 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.
|
||||
26
src/main/java/com/hierynomus/sshj/backport/JavaVersion.java
Normal file
26
src/main/java/com/hierynomus/sshj/backport/JavaVersion.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C)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 com.hierynomus.sshj.backport;
|
||||
|
||||
public class JavaVersion {
|
||||
public static boolean isJava7OrEarlier() {
|
||||
String property = System.getProperty("java.specification.version");
|
||||
float diff = Float.parseFloat(property) - 1.7f;
|
||||
|
||||
return diff < 0.01;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C)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 com.hierynomus.sshj.backport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.*;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class Jdk7HttpProxySocket extends Socket {
|
||||
|
||||
private Proxy httpProxy = null;
|
||||
|
||||
public Jdk7HttpProxySocket(Proxy proxy) {
|
||||
super(proxy.type() == Proxy.Type.HTTP ? Proxy.NO_PROXY : proxy);
|
||||
if (proxy.type() == Proxy.Type.HTTP) {
|
||||
this.httpProxy = proxy;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(SocketAddress endpoint, int timeout) throws IOException {
|
||||
if (httpProxy != null) {
|
||||
connectHttpProxy(endpoint, timeout);
|
||||
} else {
|
||||
super.connect(endpoint, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
private void connectHttpProxy(SocketAddress endpoint, int timeout) throws IOException {
|
||||
super.connect(httpProxy.address(), timeout);
|
||||
|
||||
if (!(endpoint instanceof InetSocketAddress)) {
|
||||
throw new SocketException("Expected an InetSocketAddress to connect to, got: " + endpoint);
|
||||
}
|
||||
InetSocketAddress isa = (InetSocketAddress) endpoint;
|
||||
String httpConnect = "CONNECT " + isa.getHostName() + ":" + isa.getPort() + " HTTP/1.0\n\n";
|
||||
getOutputStream().write(httpConnect.getBytes(Charset.forName("UTF-8")));
|
||||
checkAndFlushProxyResponse();
|
||||
}
|
||||
|
||||
private void checkAndFlushProxyResponse()throws IOException {
|
||||
InputStream socketInput = getInputStream();
|
||||
byte[] tmpBuffer = new byte[512];
|
||||
int len = socketInput.read(tmpBuffer, 0, tmpBuffer.length);
|
||||
|
||||
if (len == 0) {
|
||||
throw new SocketException("Empty response from proxy");
|
||||
}
|
||||
|
||||
String proxyResponse = new String(tmpBuffer, 0, len, "UTF-8");
|
||||
|
||||
// Expecting HTTP/1.x 200 OK
|
||||
if (proxyResponse.contains("200")) {
|
||||
// Flush any outstanding message in buffer
|
||||
if (socketInput.available() > 0) {
|
||||
socketInput.skip(socketInput.available());
|
||||
}
|
||||
// Proxy Connect Successful
|
||||
} else {
|
||||
throw new SocketException("Fail to create Socket\nResponse was:" + proxyResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/main/java/com/hierynomus/sshj/backport/Sockets.java
Normal file
41
src/main/java/com/hierynomus/sshj/backport/Sockets.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C)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 com.hierynomus.sshj.backport;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
|
||||
public class Sockets {
|
||||
|
||||
/**
|
||||
* Java 7 and up have Socket implemented as Closeable, whereas Java6 did not have this inheritance.
|
||||
* @param socket The socket to wrap as Closeable
|
||||
* @return The (potentially wrapped) Socket as a Closeable.
|
||||
*/
|
||||
public static Closeable asCloseable(final Socket socket) {
|
||||
if (Closeable.class.isAssignableFrom(socket.getClass())) {
|
||||
return Closeable.class.cast(socket);
|
||||
} else {
|
||||
return new Closeable() {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
socket.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
70
src/main/java/com/hierynomus/sshj/secg/SecgUtils.java
Normal file
70
src/main/java/com/hierynomus/sshj/secg/SecgUtils.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C)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 com.hierynomus.sshj.secg;
|
||||
|
||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.EllipticCurve;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SecgUtils {
|
||||
/**
|
||||
* SECG 2.3.4 Octet String to ECPoint
|
||||
*/
|
||||
public static ECPoint getDecoded(byte[] M, EllipticCurve curve) {
|
||||
int elementSize = getElementSize(curve);
|
||||
if (M.length != 2 * elementSize + 1 || M[0] != 0x04) {
|
||||
throw new SSHRuntimeException("Invalid 'f' for Elliptic Curve " + curve.toString());
|
||||
}
|
||||
byte[] xBytes = new byte[elementSize];
|
||||
byte[] yBytes = new byte[elementSize];
|
||||
System.arraycopy(M, 1, xBytes, 0, elementSize);
|
||||
System.arraycopy(M, 1 + elementSize, yBytes, 0, elementSize);
|
||||
return new ECPoint(new BigInteger(1, xBytes), new BigInteger(1, yBytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* SECG 2.3.3 ECPoint to Octet String
|
||||
*/
|
||||
public static byte[] getEncoded(ECPoint point, EllipticCurve curve) {
|
||||
int elementSize = getElementSize(curve);
|
||||
byte[] M = new byte[2 * elementSize + 1];
|
||||
M[0] = 0x04;
|
||||
|
||||
byte[] xBytes = stripLeadingZeroes(point.getAffineX().toByteArray());
|
||||
byte[] yBytes = stripLeadingZeroes(point.getAffineY().toByteArray());
|
||||
System.arraycopy(xBytes, 0, M, 1 + elementSize - xBytes.length, xBytes.length);
|
||||
System.arraycopy(yBytes, 0, M, 1 + 2 * elementSize - yBytes.length, yBytes.length);
|
||||
return M;
|
||||
}
|
||||
|
||||
private static byte[] stripLeadingZeroes(byte[] bytes) {
|
||||
int start = 0;
|
||||
while (bytes[start] == 0x0) {
|
||||
start++;
|
||||
}
|
||||
|
||||
return Arrays.copyOfRange(bytes, start, bytes.length);
|
||||
}
|
||||
|
||||
private static int getElementSize(EllipticCurve curve) {
|
||||
int fieldSize = curve.getField().getFieldSize();
|
||||
return (fieldSize + 7) / 8;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C)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 com.hierynomus.sshj.signature;
|
||||
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Our own extension of the EdDSAPublicKey that comes from ECC-25519, as that class does not implement equality.
|
||||
* The code uses the equality of the keys as an indicator whether they're the same during host key verification.
|
||||
*/
|
||||
public class Ed25519PublicKey extends EdDSAPublicKey {
|
||||
|
||||
public Ed25519PublicKey(EdDSAPublicKeySpec spec) {
|
||||
super(spec);
|
||||
|
||||
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
|
||||
if (!spec.getParams().getCurve().equals(ed25519.getCurve())) {
|
||||
throw new SSHRuntimeException("Cannot create Ed25519 Public Key from wrong spec");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Ed25519PublicKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Ed25519PublicKey otherKey = (Ed25519PublicKey) other;
|
||||
return Arrays.equals(getAbyte(), otherKey.getAbyte());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getA().hashCode();
|
||||
}
|
||||
}
|
||||
109
src/main/java/com/hierynomus/sshj/signature/SignatureEdDSA.java
Normal file
109
src/main/java/com/hierynomus/sshj/signature/SignatureEdDSA.java
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C)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 com.hierynomus.sshj.signature;
|
||||
|
||||
import net.i2p.crypto.eddsa.EdDSAEngine;
|
||||
import net.schmizz.sshj.common.Buffer;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||
import net.schmizz.sshj.signature.Signature;
|
||||
|
||||
import java.security.*;
|
||||
|
||||
public class SignatureEdDSA implements Signature {
|
||||
public static class Factory implements net.schmizz.sshj.common.Factory.Named<Signature> {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return KeyType.ED25519.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Signature create() {
|
||||
return new SignatureEdDSA();
|
||||
}
|
||||
}
|
||||
|
||||
final EdDSAEngine engine;
|
||||
|
||||
protected SignatureEdDSA() {
|
||||
try {
|
||||
engine = new EdDSAEngine(MessageDigest.getInstance("SHA-512"));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(PublicKey pubkey, PrivateKey prvkey) {
|
||||
try {
|
||||
if (pubkey != null) {
|
||||
engine.initVerify(pubkey);
|
||||
}
|
||||
|
||||
if (prvkey != null) {
|
||||
engine.initSign(prvkey);
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] H) {
|
||||
update(H, 0, H.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] H, int off, int len) {
|
||||
try {
|
||||
engine.update(H, off, len);
|
||||
} catch (SignatureException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sign() {
|
||||
try {
|
||||
return engine.sign();
|
||||
} catch (SignatureException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encode(byte[] signature) {
|
||||
return signature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(byte[] sig) {
|
||||
try {
|
||||
Buffer.PlainBuffer plainBuffer = new Buffer.PlainBuffer(sig);
|
||||
String algo = plainBuffer.readString();
|
||||
if (!"ssh-ed25519".equals(algo)) {
|
||||
throw new SSHRuntimeException("Expected 'ssh-ed25519' key algorithm, but was: " + algo);
|
||||
}
|
||||
byte[] bytes = plainBuffer.readBytes();
|
||||
return engine.verify(bytes);
|
||||
} catch (SignatureException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
} catch (Buffer.BufferException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C)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 com.hierynomus.sshj.transport.cipher;
|
||||
|
||||
import net.schmizz.sshj.transport.cipher.BlockCipher;
|
||||
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||
|
||||
/**
|
||||
* All BlockCiphers supported by SSH according to the following RFCs
|
||||
*
|
||||
* - https://tools.ietf.org/html/rfc4344#section-3.1
|
||||
* - https://tools.ietf.org/html/rfc4253#section-6.3
|
||||
*
|
||||
* TODO: https://tools.ietf.org/html/rfc5647
|
||||
*
|
||||
* Some of the Ciphers are still implemented in net.schmizz.sshj.transport.cipher.*. These are scheduled to be migrated to here.
|
||||
*/
|
||||
public class BlockCiphers {
|
||||
|
||||
public static final String COUNTER_MODE = "CTR";
|
||||
public static final String CIPHER_BLOCK_CHAINING_MODE = "CBC";
|
||||
|
||||
public static Factory BlowfishCTR() {
|
||||
return new Factory(8, 256, "blowfish-ctr", "Blowfish", COUNTER_MODE);
|
||||
}
|
||||
public static Factory Twofish128CTR() {
|
||||
return new Factory(16, 128, "twofish128-ctr", "Twofish", COUNTER_MODE);
|
||||
}
|
||||
public static Factory Twofish192CTR() {
|
||||
return new Factory(16, 192, "twofish192-ctr", "Twofish", COUNTER_MODE);
|
||||
}
|
||||
public static Factory Twofish256CTR() {
|
||||
return new Factory(16, 256, "twofish256-ctr", "Twofish", COUNTER_MODE);
|
||||
}
|
||||
public static Factory Twofish128CBC() {
|
||||
return new Factory(16, 128, "twofish128-cbc", "Twofish", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static Factory Twofish192CBC() {
|
||||
return new Factory(16, 192, "twofish192-cbc", "Twofish", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static Factory Twofish256CBC() {
|
||||
return new Factory(16, 256, "twofish256-cbc", "Twofish", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static Factory TwofishCBC() {
|
||||
return new Factory(16, 256, "twofish-cbc", "Twofish", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static Factory Serpent128CTR() {
|
||||
return new Factory(16, 128, "serpent128-ctr", "Serpent", COUNTER_MODE);
|
||||
}
|
||||
public static Factory Serpent192CTR() {
|
||||
return new Factory(16, 192, "serpent192-ctr", "Serpent", COUNTER_MODE);
|
||||
}
|
||||
public static Factory Serpent256CTR() {
|
||||
return new Factory(16, 256, "serpent256-ctr", "Serpent", COUNTER_MODE);
|
||||
}
|
||||
public static Factory Serpent128CBC() {
|
||||
return new Factory(16, 128, "serpent128-cbc", "Serpent", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static Factory Serpent192CBC() {
|
||||
return new Factory(16, 192, "serpent192-cbc", "Serpent", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static Factory Serpent256CBC() {
|
||||
return new Factory(16, 256, "serpent256-cbc", "Serpent", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static Factory IDEACTR() {
|
||||
return new Factory(8, 128, "idea-ctr", "IDEA", COUNTER_MODE);
|
||||
}
|
||||
public static Factory IDEACBC() {
|
||||
return new Factory(8, 128, "idea-cbc", "IDEA", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static Factory Cast128CTR() {
|
||||
return new Factory(8, 128, "cast128-ctr", "CAST5", COUNTER_MODE);
|
||||
}
|
||||
public static Factory Cast128CBC() {
|
||||
return new Factory(8, 128, "cast128-cbc", "CAST5", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static Factory TripleDESCTR() {
|
||||
return new Factory(8, 192, "3des-ctr", "DESede", COUNTER_MODE);
|
||||
}
|
||||
|
||||
/** Named factory for BlockCipher */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||
|
||||
private int keysize;
|
||||
private String cipher;
|
||||
private String mode;
|
||||
private String name;
|
||||
private int ivsize;
|
||||
|
||||
/**
|
||||
* @param ivsize
|
||||
* @param keysize The keysize used in bits.
|
||||
* @param name
|
||||
* @param cipher
|
||||
* @param mode
|
||||
*/
|
||||
public Factory(int ivsize, int keysize, String name, String cipher, String mode) {
|
||||
this.name = name;
|
||||
this.keysize = keysize;
|
||||
this.cipher = cipher;
|
||||
this.mode = mode;
|
||||
this.ivsize = ivsize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cipher create() {
|
||||
return new BlockCipher(ivsize, keysize / 8, cipher, cipher + "/" + mode + "/NoPadding");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C)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 com.hierynomus.sshj.transport.cipher;
|
||||
|
||||
import static com.hierynomus.sshj.transport.cipher.BlockCiphers.CIPHER_BLOCK_CHAINING_MODE;
|
||||
import static com.hierynomus.sshj.transport.cipher.BlockCiphers.COUNTER_MODE;
|
||||
|
||||
/**
|
||||
* Set of Block Ciphers that are (not yet) part of any of the official RFCs for SSH, but
|
||||
* that are either supported by other SSH implementations, or are being pushed for to be
|
||||
* included in a new RFC.
|
||||
*
|
||||
* - http://tools.ietf.org/id/draft-kanno-secsh-camellia-01.txt
|
||||
*/
|
||||
public class ExtendedBlockCiphers {
|
||||
public static BlockCiphers.Factory Camellia128CTR() {
|
||||
return new BlockCiphers.Factory(16, 128, "camellia128-ctr", "Camellia", COUNTER_MODE);
|
||||
}
|
||||
public static BlockCiphers.Factory Camellia128CTROpenSSHOrg() {
|
||||
return new BlockCiphers.Factory(16, 128, "camellia128-ctr@openssh.org", "Camellia", COUNTER_MODE);
|
||||
}
|
||||
public static BlockCiphers.Factory Camellia192CTR() {
|
||||
return new BlockCiphers.Factory(16, 192, "camellia192-ctr", "Camellia", COUNTER_MODE);
|
||||
}
|
||||
public static BlockCiphers.Factory Camellia192CTROpenSSHOrg() {
|
||||
return new BlockCiphers.Factory(16, 192, "camellia192-ctr@openssh.org", "Camellia", COUNTER_MODE);
|
||||
}
|
||||
public static BlockCiphers.Factory Camellia256CTR() {
|
||||
return new BlockCiphers.Factory(16, 256, "camellia256-ctr", "Camellia", COUNTER_MODE);
|
||||
}
|
||||
public static BlockCiphers.Factory Camellia256CTROpenSSHOrg() {
|
||||
return new BlockCiphers.Factory(16, 256, "camellia256-ctr@openssh.org", "Camellia", COUNTER_MODE);
|
||||
}
|
||||
public static BlockCiphers.Factory Camellia128CBC() {
|
||||
return new BlockCiphers.Factory(16, 128, "camellia128-cbc", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static BlockCiphers.Factory Camellia128CBCOpenSSHOrg() {
|
||||
return new BlockCiphers.Factory(16, 128, "camellia128-cbc@openssh.org", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static BlockCiphers.Factory Camellia192CBC() {
|
||||
return new BlockCiphers.Factory(16, 192, "camellia192-cbc", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static BlockCiphers.Factory Camellia192CBCOpenSSHOrg() {
|
||||
return new BlockCiphers.Factory(16, 192, "camellia192-cbc@openssh.org", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static BlockCiphers.Factory Camellia256CBC() {
|
||||
return new BlockCiphers.Factory(16, 256, "camellia256-cbc", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
public static BlockCiphers.Factory Camellia256CBCOpenSSHOrg() {
|
||||
return new BlockCiphers.Factory(16, 256, "camellia256-cbc@openssh.org", "Camellia", CIPHER_BLOCK_CHAINING_MODE);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C)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 com.hierynomus.sshj.transport.cipher;
|
||||
|
||||
import net.schmizz.sshj.transport.cipher.BaseCipher;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public class StreamCipher extends BaseCipher {
|
||||
|
||||
public StreamCipher(int bsize, String algorithm, String transformation) {
|
||||
super(0, bsize, algorithm, transformation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initCipher(javax.crypto.Cipher cipher, Mode mode, byte[] key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
cipher.init(getMode(mode), getKeySpec(key), new SecureRandom());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C)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 com.hierynomus.sshj.transport.cipher;
|
||||
|
||||
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||
|
||||
/**
|
||||
* Implementations of the Stream Ciphers that are defined in the RFCs
|
||||
*
|
||||
* - https://tools.ietf.org/html/rfc4253#section-6.3
|
||||
* - https://tools.ietf.org/html/rfc4345
|
||||
*/
|
||||
public class StreamCiphers {
|
||||
|
||||
public static Factory Arcfour() {
|
||||
return new Factory(128, "arcfour", "ARCFOUR", "ECB");
|
||||
}
|
||||
public static Factory Arcfour128() {
|
||||
return new Factory(128, "arcfour128", "RC4", "ECB");
|
||||
}
|
||||
public static Factory Arcfour256() {
|
||||
return new Factory(256, "arcfour256", "RC4", "ECB");
|
||||
}
|
||||
|
||||
/** Named factory for BlockCipher */
|
||||
public static class Factory
|
||||
implements net.schmizz.sshj.common.Factory.Named<Cipher> {
|
||||
|
||||
private int keysize;
|
||||
private String cipher;
|
||||
private String mode;
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* @param keysize The keysize used in bits.
|
||||
* @param name
|
||||
* @param cipher
|
||||
* @param mode
|
||||
*/
|
||||
public Factory(int keysize, String name, String cipher, String mode) {
|
||||
this.name = name;
|
||||
this.keysize = keysize;
|
||||
this.cipher = cipher;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cipher create() {
|
||||
return new StreamCipher(keysize / 8, cipher, cipher + "/" + mode + "/NoPadding");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +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 examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session.Command;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** This examples demonstrates how a remote command can be executed. */
|
||||
public class Exec {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
ssh.loadKnownHosts();
|
||||
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
|
||||
Command cmd = ssh.startSession().exec("ping google.com -n 1");
|
||||
|
||||
// Pipe.pipe(cmd.getInputStream(), System.out, cmd.getLocalMaxPacketSize(), false);
|
||||
System.out.print(cmd.getOutputAsString());
|
||||
System.out.println("\n** exit status: " + cmd.getExitStatus());
|
||||
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +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 examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* This example demonstrates local port forwarding, i.e. when we listen on a particular address and port; and forward
|
||||
* all incoming connections to SSH server which further forwards them to a specified address and port.
|
||||
*/
|
||||
public class LocalPF {
|
||||
|
||||
// static
|
||||
// {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
|
||||
ssh.loadKnownHosts();
|
||||
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
|
||||
/*
|
||||
* _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();
|
||||
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +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 examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import net.schmizz.sshj.common.StreamCopier;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session;
|
||||
import net.schmizz.sshj.connection.channel.direct.Session.Shell;
|
||||
import net.schmizz.sshj.transport.verification.ConsoleKnownHostsVerifier;
|
||||
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/** A very rudimentary psuedo-terminal based on console I/O. */
|
||||
class RudimentaryPTY {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String... args)
|
||||
throws IOException {
|
||||
|
||||
final SSHClient ssh = new SSHClient();
|
||||
|
||||
final File khFile = new File(OpenSSHKnownHosts.detectSSHDir(), "known_hosts");
|
||||
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();
|
||||
|
||||
shell = session.startShell();
|
||||
|
||||
new StreamCopier("stdout", shell.getInputStream(), System.out)
|
||||
.bufSize(shell.getLocalMaxPacketSize())
|
||||
.start();
|
||||
|
||||
new StreamCopier("stderr", shell.getErrorStream(), System.err)
|
||||
.bufSize(shell.getLocalMaxPacketSize())
|
||||
.start();
|
||||
|
||||
// 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);
|
||||
|
||||
} finally {
|
||||
|
||||
if (shell != null)
|
||||
shell.close();
|
||||
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +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 examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** This example demonstrates downloading of a file over SCP from the SSH server. */
|
||||
public class SCPDownload {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
// ssh.useCompression(); // Can lead to significant speedup
|
||||
ssh.loadKnownHosts();
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
ssh.newSCPFileTransfer()
|
||||
.download("well", "/tmp/");
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +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 examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** This example demonstrates uploading of a file over SCP to the SSH server. */
|
||||
public class SCPUpload {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException, ClassNotFoundException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
ssh.loadKnownHosts();
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
|
||||
// Present here to demo algorithm renegotiation - could have just put this before connect()
|
||||
ssh.useCompression();
|
||||
|
||||
ssh.newSCPFileTransfer()
|
||||
.upload("/Users/shikhar/well", "/tmp/");
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +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 examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** This example demonstrates downloading of a file over SFTP from the SSH server. */
|
||||
public class SFTPDownload {
|
||||
|
||||
// static {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
ssh.loadKnownHosts();
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
ssh.newSFTPClient().get("well", "/tmp/");
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +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 examples;
|
||||
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** This example demonstrates uploading of a file over SFTP to the SSH server. */
|
||||
public class SFTPUpload {
|
||||
|
||||
// static
|
||||
// {
|
||||
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
|
||||
// }
|
||||
|
||||
public static void main(String[] args)
|
||||
throws IOException {
|
||||
SSHClient ssh = new SSHClient();
|
||||
ssh.loadKnownHosts();
|
||||
ssh.connect("localhost");
|
||||
try {
|
||||
ssh.authPublickey(System.getProperty("user.name"));
|
||||
ssh.newSFTPClient().put("/Users/shikhar/well", "/tmp/");
|
||||
} finally {
|
||||
ssh.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
42
src/main/java/net/schmizz/concurrent/ErrorDeliveryUtil.java
Normal file
42
src/main/java/net/schmizz/concurrent/ErrorDeliveryUtil.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C)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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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.
|
||||
@@ -37,4 +37,4 @@ public interface ExceptionChainer<Z extends Throwable> {
|
||||
|
||||
Z chain(Throwable t);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
240
src/main/java/net/schmizz/concurrent/Promise.java
Normal file
240
src/main/java/net/schmizz/concurrent/Promise.java
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C)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;
|
||||
}
|
||||
|
||||
}
|
||||
34
src/main/java/net/schmizz/keepalive/Heartbeater.java
Normal file
34
src/main/java/net/schmizz/keepalive/Heartbeater.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C)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));
|
||||
}
|
||||
}
|
||||
81
src/main/java/net/schmizz/keepalive/KeepAlive.java
Normal file
81
src/main/java/net/schmizz/keepalive/KeepAlive.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C)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;
|
||||
}
|
||||
39
src/main/java/net/schmizz/keepalive/KeepAliveProvider.java
Normal file
39
src/main/java/net/schmizz/keepalive/KeepAliveProvider.java
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C)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);
|
||||
|
||||
|
||||
}
|
||||
76
src/main/java/net/schmizz/keepalive/KeepAliveRunner.java
Normal file
76
src/main/java/net/schmizz/keepalive/KeepAliveRunner.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C)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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,38 +35,35 @@ 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
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Message msg, SSHPacket buf)
|
||||
throws SSHException {
|
||||
trans.sendUnimplemented();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyError(SSHException error) {
|
||||
log.debug("Was notified of {}", error.toString());
|
||||
log.debug("Notified of {}", error.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyUnimplemented(long seqNum)
|
||||
throws SSHException {
|
||||
throw new SSHException(DisconnectReason.PROTOCOL_ERROR, "Unexpected: SSH_MSG_UNIMPLEMENTED");
|
||||
}
|
||||
|
||||
public void notifyDisconnect()
|
||||
throws SSHException {
|
||||
log.debug("Was notified of disconnect");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void request()
|
||||
throws TransportException {
|
||||
final Service active = trans.getService();
|
||||
@@ -77,12 +74,6 @@ public abstract class AbstractService
|
||||
trans.reqService(this);
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return this.timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
29
src/main/java/net/schmizz/sshj/AndroidConfig.java
Normal file
29
src/main/java/net/schmizz/sshj/AndroidConfig.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C)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;
|
||||
|
||||
import net.schmizz.sshj.transport.random.JCERandom;
|
||||
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
|
||||
|
||||
public class AndroidConfig
|
||||
extends DefaultConfig {
|
||||
|
||||
@Override
|
||||
protected void initRandomFactory(boolean ignored) {
|
||||
setRandomFactory(new SingletonRandomFactory(new JCERandom.Factory()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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();
|
||||
|
||||
@@ -142,5 +144,35 @@ public interface Config {
|
||||
* @param version software version info
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Gets whether the client should first wait for a received server ident, before sending the client ident.
|
||||
* <p/>
|
||||
* <stong>NB:</stong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
|
||||
*
|
||||
* The default value is set to false.
|
||||
*
|
||||
* @return Whether to first wait for the server ident.
|
||||
*/
|
||||
boolean isWaitForServerIdentBeforeSendingClientIdent();
|
||||
|
||||
/**
|
||||
* Sets whether the SSH client should wait for a received server ident, before sending the client ident.
|
||||
* <p/>
|
||||
* <stong>NB:</stong> This is non-standard behaviour, and can potentially deadlock if the server also waits on the client ident.
|
||||
|
||||
* @param waitForServerIdentBeforeSendingClientIdent Whether to wait for the server ident.
|
||||
*/
|
||||
void setWaitForServerIdentBeforeSendingClientIdent(boolean waitForServerIdentBeforeSendingClientIdent);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
@@ -62,34 +44,44 @@ public class ConfigImpl
|
||||
private List<Factory.Named<Signature>> signatureFactories;
|
||||
private List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories;
|
||||
|
||||
private boolean waitForServerIdentBeforeSendingClientIdent = false;
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<Cipher>> getCipherFactories() {
|
||||
return cipherFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<Compression>> getCompressionFactories() {
|
||||
return compressionFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<FileKeyProvider>> getFileKeyProviderFactories() {
|
||||
return fileKeyProviderFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<KeyExchange>> getKeyExchangeFactories() {
|
||||
return kexFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<MAC>> getMACFactories() {
|
||||
return macFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Factory<Random> getRandomFactory() {
|
||||
return randomFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Factory.Named<Signature>> getSignatureFactories() {
|
||||
return signatureFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
@@ -98,6 +90,7 @@ public class ConfigImpl
|
||||
setCipherFactories(Arrays.asList(cipherFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCipherFactories(List<Factory.Named<Cipher>> cipherFactories) {
|
||||
this.cipherFactories = cipherFactories;
|
||||
}
|
||||
@@ -106,6 +99,7 @@ public class ConfigImpl
|
||||
setCompressionFactories(Arrays.asList(compressionFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCompressionFactories(List<Factory.Named<Compression>> compressionFactories) {
|
||||
this.compressionFactories = compressionFactories;
|
||||
}
|
||||
@@ -114,6 +108,7 @@ public class ConfigImpl
|
||||
setFileKeyProviderFactories(Arrays.asList(fileKeyProviderFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFileKeyProviderFactories(List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories) {
|
||||
this.fileKeyProviderFactories = fileKeyProviderFactories;
|
||||
}
|
||||
@@ -122,6 +117,7 @@ public class ConfigImpl
|
||||
setKeyExchangeFactories(Arrays.asList(kexFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeyExchangeFactories(List<Factory.Named<KeyExchange>> kexFactories) {
|
||||
this.kexFactories = kexFactories;
|
||||
}
|
||||
@@ -130,10 +126,12 @@ public class ConfigImpl
|
||||
setMACFactories(Arrays.asList(macFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMACFactories(List<Factory.Named<MAC>> macFactories) {
|
||||
this.macFactories = macFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRandomFactory(Factory<Random> randomFactory) {
|
||||
this.randomFactory = randomFactory;
|
||||
}
|
||||
@@ -142,12 +140,33 @@ public class ConfigImpl
|
||||
setSignatureFactories(Arrays.asList(signatureFactories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSignatureFactories(List<Factory.Named<Signature>> signatureFactories) {
|
||||
this.signatureFactories = signatureFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public KeepAliveProvider getKeepAliveProvider() {
|
||||
return keepAliveProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeepAliveProvider(KeepAliveProvider keepAliveProvider) {
|
||||
this.keepAliveProvider = keepAliveProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWaitForServerIdentBeforeSendingClientIdent() {
|
||||
return waitForServerIdentBeforeSendingClientIdent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWaitForServerIdentBeforeSendingClientIdent(boolean waitForServerIdentBeforeSendingClientIdent) {
|
||||
this.waitForServerIdentBeforeSendingClientIdent = waitForServerIdentBeforeSendingClientIdent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,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;
|
||||
|
||||
import com.hierynomus.sshj.signature.SignatureEdDSA;
|
||||
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
|
||||
import com.hierynomus.sshj.transport.cipher.StreamCiphers;
|
||||
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;
|
||||
@@ -50,38 +34,47 @@ import net.schmizz.sshj.transport.cipher.BlowfishCBC;
|
||||
import net.schmizz.sshj.transport.cipher.Cipher;
|
||||
import net.schmizz.sshj.transport.cipher.TripleDESCBC;
|
||||
import net.schmizz.sshj.transport.compression.NoneCompression;
|
||||
import net.schmizz.sshj.transport.kex.DHG1;
|
||||
import net.schmizz.sshj.transport.kex.DHG14;
|
||||
import net.schmizz.sshj.transport.kex.*;
|
||||
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;
|
||||
|
||||
import java.awt.image.ByteLookupTable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
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 +84,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_14_0";
|
||||
|
||||
public DefaultConfig() {
|
||||
setVersion(VERSION);
|
||||
@@ -103,22 +96,31 @@ public class DefaultConfig
|
||||
initCompressionFactories();
|
||||
initMACFactories();
|
||||
initSignatureFactories();
|
||||
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
|
||||
}
|
||||
|
||||
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
|
||||
if (bouncyCastleRegistered)
|
||||
setKeyExchangeFactories(new DHG14.Factory(), new DHG1.Factory());
|
||||
setKeyExchangeFactories(new Curve25519SHA256.Factory(),
|
||||
new DHGexSHA256.Factory(),
|
||||
new ECDHNistP.Factory521(),
|
||||
new ECDHNistP.Factory384(),
|
||||
new ECDHNistP.Factory256(),
|
||||
new DHGexSHA1.Factory(),
|
||||
new DHG14.Factory(),
|
||||
new DHG1.Factory());
|
||||
else
|
||||
setKeyExchangeFactories(new DHG1.Factory());
|
||||
setKeyExchangeFactories(new DHG1.Factory(), new DHGexSHA1.Factory());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,11 +134,34 @@ public class DefaultConfig
|
||||
new AES192CBC.Factory(),
|
||||
new AES256CBC.Factory(),
|
||||
new TripleDESCBC.Factory(),
|
||||
new BlowfishCBC.Factory()));
|
||||
new BlowfishCBC.Factory(),
|
||||
BlockCiphers.BlowfishCTR(),
|
||||
BlockCiphers.Cast128CBC(),
|
||||
BlockCiphers.Cast128CTR(),
|
||||
BlockCiphers.IDEACBC(),
|
||||
BlockCiphers.IDEACTR(),
|
||||
BlockCiphers.Serpent128CBC(),
|
||||
BlockCiphers.Serpent128CTR(),
|
||||
BlockCiphers.Serpent192CBC(),
|
||||
BlockCiphers.Serpent192CTR(),
|
||||
BlockCiphers.Serpent256CBC(),
|
||||
BlockCiphers.Serpent256CTR(),
|
||||
BlockCiphers.TripleDESCTR(),
|
||||
BlockCiphers.Twofish128CBC(),
|
||||
BlockCiphers.Twofish128CTR(),
|
||||
BlockCiphers.Twofish192CBC(),
|
||||
BlockCiphers.Twofish192CTR(),
|
||||
BlockCiphers.Twofish256CBC(),
|
||||
BlockCiphers.Twofish256CTR(),
|
||||
BlockCiphers.TwofishCBC(),
|
||||
StreamCiphers.Arcfour(),
|
||||
StreamCiphers.Arcfour128(),
|
||||
StreamCiphers.Arcfour256()));
|
||||
|
||||
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 +169,25 @@ 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;
|
||||
log.warn(e.getCause().getMessage());
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
if (warn)
|
||||
log.warn("Disabling high-strength ciphers: cipher strengths apparently limited by JCE policy");
|
||||
|
||||
setCipherFactories(avail);
|
||||
log.debug("Available cipher factories: {}", avail);
|
||||
}
|
||||
|
||||
protected void initSignatureFactories() {
|
||||
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory());
|
||||
setSignatureFactories(new SignatureECDSA.Factory(), new SignatureRSA.Factory(), new SignatureDSA.Factory(), new SignatureEdDSA.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() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
@@ -40,30 +39,43 @@ import net.schmizz.sshj.transport.TransportImpl;
|
||||
import net.schmizz.sshj.transport.compression.DelayedZlibCompression;
|
||||
import net.schmizz.sshj.transport.compression.NoneCompression;
|
||||
import net.schmizz.sshj.transport.compression.ZlibCompression;
|
||||
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
|
||||
import net.schmizz.sshj.transport.verification.HostKeyVerifier;
|
||||
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
|
||||
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;
|
||||
import net.schmizz.sshj.userauth.method.AuthGssApiWithMic;
|
||||
import net.schmizz.sshj.userauth.method.AuthKeyboardInteractive;
|
||||
import net.schmizz.sshj.userauth.method.AuthMethod;
|
||||
import net.schmizz.sshj.userauth.method.AuthPassword;
|
||||
import net.schmizz.sshj.userauth.method.AuthPublickey;
|
||||
import net.schmizz.sshj.userauth.method.PasswordResponseProvider;
|
||||
import net.schmizz.sshj.userauth.password.PasswordFinder;
|
||||
import net.schmizz.sshj.userauth.password.PasswordUpdateProvider;
|
||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||
import net.schmizz.sshj.userauth.password.Resource;
|
||||
import net.schmizz.sshj.xfer.scp.SCPFileTransfer;
|
||||
import org.ietf.jgss.Oid;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.security.auth.login.LoginContext;
|
||||
|
||||
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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -87,19 +99,21 @@ import java.util.List;
|
||||
* <p/>
|
||||
* <em>A simple example:</em>
|
||||
* <p/>
|
||||
* <p/>
|
||||
* <pre>
|
||||
* client = new SSHClient();
|
||||
* client.initUserKnownHosts();
|
||||
* final SSHClient client = new SSHClient();
|
||||
* client.loadKnownHosts();
|
||||
* client.connect("hostname");
|
||||
* try
|
||||
* {
|
||||
* try {
|
||||
* client.authPassword("username", "password");
|
||||
* client.startSession().exec("true");
|
||||
* client.getConnection().join();
|
||||
* } finally
|
||||
* {
|
||||
* client.disconnect();
|
||||
* final Session session = client.startSession();
|
||||
* try {
|
||||
* final Command cmd = session.exec("true");
|
||||
* cmd.join(1, TimeUnit.SECONDS);
|
||||
* } finally {
|
||||
* session.close();
|
||||
* }
|
||||
* } finally {
|
||||
* client.disconnect();
|
||||
* }
|
||||
* </pre>
|
||||
* <p/>
|
||||
@@ -108,7 +122,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;
|
||||
@@ -116,7 +130,6 @@ public class SSHClient
|
||||
/** Logger */
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
|
||||
/** Transport layer */
|
||||
protected final Transport trans;
|
||||
|
||||
@@ -138,39 +151,49 @@ 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link HostKeyVerifier} which will be invoked for verifying host key during connection establishment and
|
||||
* future key exchanges.
|
||||
*
|
||||
* @param hostKeyVerifier {@link HostKeyVerifier} instance
|
||||
* @param verifier {@link HostKeyVerifier} instance
|
||||
*/
|
||||
public void addHostKeyVerifier(HostKeyVerifier hostKeyVerifier) {
|
||||
trans.addHostKeyVerifier(hostKeyVerifier);
|
||||
public void addHostKeyVerifier(HostKeyVerifier verifier) {
|
||||
trans.addHostKeyVerifier(verifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 AlgorithmsVerifier} which will be invoked for verifying negotiated algorithms.
|
||||
*
|
||||
* @param verifier {@link AlgorithmsVerifier} instance
|
||||
*/
|
||||
public void addAlgorithmsVerifier(AlgorithmsVerifier verifier) {
|
||||
trans.addAlgorithmsVerifier(verifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*
|
||||
@@ -182,7 +205,7 @@ public class SSHClient
|
||||
*/
|
||||
public void auth(String username, AuthMethod... methods)
|
||||
throws UserAuthException, TransportException {
|
||||
assert isConnected();
|
||||
checkConnected();
|
||||
auth(username, Arrays.<AuthMethod>asList(methods));
|
||||
}
|
||||
|
||||
@@ -197,41 +220,22 @@ 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<>();
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method. The {@code password} array is
|
||||
* blanked out after use.
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param password the password to use for authentication
|
||||
*
|
||||
* @throws UserAuthException in case of authentication failure
|
||||
* @throws TransportException if there was a transport-layer error
|
||||
*/
|
||||
public void authPassword(String username, char[] password)
|
||||
throws UserAuthException, TransportException {
|
||||
authPassword(username, PasswordUtils.createOneOff(password));
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method.
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param pfinder the {@link PasswordFinder} to use for authentication
|
||||
*
|
||||
* @throws UserAuthException in case of authentication failure
|
||||
* @throws TransportException if there was a transport-layer error
|
||||
*/
|
||||
public void authPassword(String username, PasswordFinder pfinder)
|
||||
throws UserAuthException, TransportException {
|
||||
auth(username, new AuthPassword(pfinder));
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method.
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method and as a fallback basic
|
||||
* challenge-response authentication.
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param password the password to use for authentication
|
||||
@@ -244,6 +248,68 @@ public class SSHClient
|
||||
authPassword(username, password.toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method and as a fallback basic
|
||||
* challenge-response authentication.. The {@code password} array is blanked out after use.
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param password the password to use for authentication
|
||||
*
|
||||
* @throws UserAuthException in case of authentication failure
|
||||
* @throws TransportException if there was a transport-layer error
|
||||
*/
|
||||
public void authPassword(final String username, final char[] password)
|
||||
throws UserAuthException, TransportException {
|
||||
try {
|
||||
authPassword(username, new PasswordFinder() {
|
||||
|
||||
@Override
|
||||
public char[] reqPassword(Resource<?> resource) {
|
||||
return password.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRetry(Resource<?> resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
} finally {
|
||||
PasswordUtils.blankOut(password);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method and as a fallback basic
|
||||
* challenge-response authentication.
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param pfinder the {@link PasswordFinder} to use for authentication
|
||||
*
|
||||
* @throws UserAuthException in case of authentication failure
|
||||
* @throws TransportException if there was a transport-layer error
|
||||
*/
|
||||
public void authPassword(String username, PasswordFinder pfinder)
|
||||
throws UserAuthException, TransportException {
|
||||
auth(username, new AuthPassword(pfinder), new AuthKeyboardInteractive(new PasswordResponseProvider(pfinder)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "password"} authentication method and as a fallback basic
|
||||
* challenge-response authentication.
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param pfinder the {@link PasswordFinder} to use for authentication
|
||||
* @param newPasswordProvider the {@link PasswordUpdateProvider} to use when a new password is being requested from the user.
|
||||
*
|
||||
* @throws UserAuthException in case of authentication failure
|
||||
* @throws TransportException if there was a transport-layer error
|
||||
*/
|
||||
public void authPassword(String username, PasswordFinder pfinder, PasswordUpdateProvider newPasswordProvider)
|
||||
throws UserAuthException, TransportException {
|
||||
auth(username, new AuthPassword(pfinder, newPasswordProvider), new AuthKeyboardInteractive(new PasswordResponseProvider(pfinder)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "publickey"} authentication method, with keys from some common
|
||||
* locations on the file system. This method relies on {@code ~/.ssh/id_rsa} and {@code ~/.ssh/id_dsa}.
|
||||
@@ -275,9 +341,8 @@ public class SSHClient
|
||||
* @throws TransportException if there was a transport-layer error
|
||||
*/
|
||||
public void authPublickey(String username, Iterable<KeyProvider> keyProviders)
|
||||
throws UserAuthException,
|
||||
TransportException {
|
||||
final List<AuthMethod> am = new LinkedList<AuthMethod>();
|
||||
throws UserAuthException, TransportException {
|
||||
final List<AuthMethod> am = new LinkedList<>();
|
||||
for (KeyProvider kp : keyProviders)
|
||||
am.add(new AuthPublickey(kp));
|
||||
auth(username, am);
|
||||
@@ -320,17 +385,42 @@ public class SSHClient
|
||||
*/
|
||||
public void authPublickey(String username, String... locations)
|
||||
throws UserAuthException, TransportException {
|
||||
final List<KeyProvider> keyProviders = new LinkedList<KeyProvider>();
|
||||
for (String loc : locations)
|
||||
final List<KeyProvider> keyProviders = new LinkedList<>();
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate {@code username} using the {@code "gssapi-with-mic"} authentication method, given a login context
|
||||
* for the peer GSS machine and a list of supported OIDs.
|
||||
* <p/>
|
||||
* Supported OIDs should be ordered by preference as the SSH server will choose the first OID that it also
|
||||
* supports. At least one OID is required
|
||||
*
|
||||
* @param username user to authenticate
|
||||
* @param context {@code LoginContext} for the peer GSS machine
|
||||
* @param supportedOid first supported OID
|
||||
* @param supportedOids other supported OIDs
|
||||
*
|
||||
* @throws UserAuthException in case of authentication failure
|
||||
* @throws TransportException if there was a transport-layer error
|
||||
*/
|
||||
public void authGssApiWithMic(String username, LoginContext context, Oid supportedOid, Oid... supportedOids)
|
||||
throws UserAuthException, TransportException {
|
||||
// insert supportedOid to the front of the list since ordering matters
|
||||
List<Oid> oids = new ArrayList<>(Arrays.asList(supportedOids));
|
||||
oids.add(0, supportedOid);
|
||||
|
||||
auth(username, new AuthGssApiWithMic(context, oids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects from the connected SSH server. {@code SSHClient} objects are not reusable therefore it is incorrect
|
||||
* to attempt connection after this method has been called.
|
||||
@@ -341,10 +431,8 @@ public class SSHClient
|
||||
@Override
|
||||
public void disconnect()
|
||||
throws IOException {
|
||||
assert isConnected();
|
||||
trans.disconnect();
|
||||
super.disconnect();
|
||||
assert !isConnected();
|
||||
}
|
||||
|
||||
/** @return the associated {@link Connection} instance. */
|
||||
@@ -370,8 +458,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;
|
||||
@@ -420,7 +507,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
|
||||
@@ -454,9 +541,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);
|
||||
@@ -480,6 +567,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
|
||||
@@ -520,23 +634,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -561,7 +673,8 @@ public class SSHClient
|
||||
|
||||
/** @return Instantiated {@link SCPFileTransfer} implementation. */
|
||||
public SCPFileTransfer newSCPFileTransfer() {
|
||||
assert isConnected() && isAuthenticated();
|
||||
checkConnected();
|
||||
checkAuthenticated();
|
||||
return new SCPFileTransfer(this);
|
||||
}
|
||||
|
||||
@@ -573,8 +686,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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -587,12 +701,13 @@ public class SSHClient
|
||||
doKex();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@@ -607,7 +722,7 @@ public class SSHClient
|
||||
* @throws TransportException if an error occurs during renegotiation
|
||||
*/
|
||||
public void useCompression()
|
||||
throws ClassNotFoundException, TransportException {
|
||||
throws TransportException {
|
||||
trans.getConfig().setCompressionFactories(Arrays.asList(
|
||||
new DelayedZlibCompression.Factory(),
|
||||
new ZlibCompression.Factory(),
|
||||
@@ -632,18 +747,33 @@ public class SSHClient
|
||||
*/
|
||||
protected void doKex()
|
||||
throws TransportException {
|
||||
assert trans.isRunning();
|
||||
|
||||
checkConnected();
|
||||
final long start = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
trans.doKex();
|
||||
} catch (TransportException te) {
|
||||
trans.disconnect(DisconnectReason.KEY_EXCHANGE_FAILED);
|
||||
throw te;
|
||||
}
|
||||
|
||||
log.info("Key exchange took {} seconds", (System.currentTimeMillis() - start) / 1000.0);
|
||||
trans.doKex();
|
||||
log.debug("Key exchange took {} seconds", (System.currentTimeMillis() - start) / 1000.0);
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Same as {@link #disconnect()}.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
private void checkConnected() {
|
||||
if (!isConnected()) {
|
||||
throw new IllegalStateException("Not connected");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAuthenticated() {
|
||||
if (!isAuthenticated()) {
|
||||
throw new IllegalStateException("Not authenticated");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,22 @@
|
||||
* 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 com.hierynomus.sshj.backport.JavaVersion;
|
||||
import com.hierynomus.sshj.backport.Jdk7HttpProxySocket;
|
||||
|
||||
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 +36,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;
|
||||
@@ -67,21 +48,53 @@ abstract class SocketClient {
|
||||
this.defaultPort = defaultPort;
|
||||
}
|
||||
|
||||
public void connect(InetAddress host, int port)
|
||||
throws IOException {
|
||||
public void connect(InetAddress host, int port) throws IOException {
|
||||
socket = socketFactory.createSocket();
|
||||
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
||||
onConnect();
|
||||
}
|
||||
|
||||
public void connect(String hostname, int port)
|
||||
throws IOException {
|
||||
|
||||
/**
|
||||
* Connect to a host via a proxy.
|
||||
* @param host The host address to connect to.
|
||||
* @param port The port to connect to.
|
||||
* @param proxy The proxy to connect via.
|
||||
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
|
||||
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
|
||||
*/
|
||||
@Deprecated
|
||||
public void connect(InetAddress host, int port, Proxy proxy) throws IOException {
|
||||
if (JavaVersion.isJava7OrEarlier() && proxy.type() == Proxy.Type.HTTP) {
|
||||
// Java7 and earlier have no support for HTTP Connect proxies, return our custom socket.
|
||||
socket = new Jdk7HttpProxySocket(proxy);
|
||||
} else {
|
||||
socket = new Socket(proxy);
|
||||
}
|
||||
socket.connect(new InetSocketAddress(host, port), connectTimeout);
|
||||
onConnect();
|
||||
}
|
||||
|
||||
public void connect(String hostname, int port) throws IOException {
|
||||
this.hostname = hostname;
|
||||
connect(InetAddress.getByName(hostname), port);
|
||||
}
|
||||
|
||||
public void connect(InetAddress host, int port,
|
||||
InetAddress localAddr, int localPort)
|
||||
/**
|
||||
* Connect to a host via a proxy.
|
||||
* @param hostname The host name to connect to.
|
||||
* @param port The port to connect to.
|
||||
* @param proxy The proxy to connect via.
|
||||
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
|
||||
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
|
||||
*/
|
||||
@Deprecated
|
||||
public void connect(String hostname, int port, Proxy proxy) throws IOException {
|
||||
this.hostname = hostname;
|
||||
connect(InetAddress.getByName(hostname), port, proxy);
|
||||
}
|
||||
|
||||
public void connect(InetAddress host, int port, InetAddress localAddr, int localPort)
|
||||
throws IOException {
|
||||
socket = socketFactory.createSocket();
|
||||
socket.bind(new InetSocketAddress(localAddr, localPort));
|
||||
@@ -89,25 +102,44 @@ abstract class SocketClient {
|
||||
onConnect();
|
||||
}
|
||||
|
||||
public void connect(String hostname, int port,
|
||||
InetAddress localAddr, int localPort)
|
||||
throws IOException {
|
||||
public void connect(String hostname, int port, InetAddress localAddr, int localPort) throws IOException {
|
||||
this.hostname = hostname;
|
||||
connect(InetAddress.getByName(hostname), port, localAddr, localPort);
|
||||
}
|
||||
|
||||
public void connect(InetAddress host)
|
||||
throws IOException {
|
||||
public void connect(InetAddress host) throws IOException {
|
||||
connect(host, defaultPort);
|
||||
}
|
||||
|
||||
public void connect(String hostname)
|
||||
throws IOException {
|
||||
public void connect(String hostname) throws IOException {
|
||||
connect(hostname, defaultPort);
|
||||
}
|
||||
|
||||
public void disconnect()
|
||||
throws IOException {
|
||||
/**
|
||||
* Connect to a host via a proxy.
|
||||
* @param host The host address to connect to.
|
||||
* @param proxy The proxy to connect via.
|
||||
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
|
||||
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
|
||||
*/
|
||||
@Deprecated
|
||||
public void connect(InetAddress host, Proxy proxy) throws IOException {
|
||||
connect(host, defaultPort, proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to a host via a proxy.
|
||||
* @param hostname The host name to connect to.
|
||||
* @param proxy The proxy to connect via.
|
||||
* @deprecated This method will be removed after v0.12.0. If you want to connect via a proxy, you can do this by injecting a {@link javax.net.SocketFactory}
|
||||
* into the SocketClient. The SocketFactory should create sockets using the {@link java.net.Socket#Socket(java.net.Proxy)} constructor.
|
||||
*/
|
||||
@Deprecated
|
||||
public void connect(String hostname, Proxy proxy) throws IOException {
|
||||
connect(hostname, defaultPort, proxy);
|
||||
}
|
||||
|
||||
public void disconnect() throws IOException {
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
socket = null;
|
||||
@@ -120,8 +152,6 @@ abstract class SocketClient {
|
||||
output.close();
|
||||
output = null;
|
||||
}
|
||||
input = null;
|
||||
output = null;
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
@@ -132,7 +162,6 @@ abstract class SocketClient {
|
||||
return socket.getLocalPort();
|
||||
}
|
||||
|
||||
|
||||
public InetAddress getLocalAddress() {
|
||||
return socket.getLocalAddress();
|
||||
}
|
||||
@@ -150,27 +179,17 @@ abstract class SocketClient {
|
||||
}
|
||||
|
||||
public void setSocketFactory(SocketFactory factory) {
|
||||
if (factory == null)
|
||||
if (factory == null) {
|
||||
socketFactory = SocketFactory.getDefault();
|
||||
else
|
||||
} else {
|
||||
socketFactory = factory;
|
||||
}
|
||||
}
|
||||
|
||||
public SocketFactory getSocketFactory() {
|
||||
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;
|
||||
}
|
||||
@@ -199,11 +218,10 @@ abstract class SocketClient {
|
||||
return output;
|
||||
}
|
||||
|
||||
void onConnect()
|
||||
throws IOException {
|
||||
void onConnect() throws IOException {
|
||||
socket.setSoTimeout(timeout);
|
||||
input = socket.getInputStream();
|
||||
output = socket.getOutputStream();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright (C)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.");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,50 +12,26 @@
|
||||
* 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.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.DSAPublicKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.DSAPublicKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
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() {
|
||||
@@ -78,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;
|
||||
}
|
||||
|
||||
@@ -144,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");
|
||||
}
|
||||
@@ -182,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;
|
||||
}
|
||||
|
||||
@@ -202,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++];
|
||||
}
|
||||
@@ -226,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];
|
||||
@@ -256,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;
|
||||
@@ -299,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,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);
|
||||
@@ -335,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);
|
||||
@@ -399,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);
|
||||
@@ -419,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();
|
||||
}
|
||||
|
||||
@@ -432,84 +398,44 @@ 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 passwd (null-ok) the password as a character array
|
||||
* @param str (null-ok) the string as a character array
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T putPassword(char[] passwd) {
|
||||
if (passwd == null)
|
||||
public T putSensitiveString(char[] str) {
|
||||
if (str == null)
|
||||
return putString("");
|
||||
putInt(passwd.length);
|
||||
ensureCapacity(passwd.length);
|
||||
for (char c : passwd)
|
||||
putUInt32(str.length);
|
||||
ensureCapacity(str.length);
|
||||
for (char c : str)
|
||||
data[wpos++] = (byte) c;
|
||||
Arrays.fill(passwd, ' ');
|
||||
Arrays.fill(str, ' ');
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public PublicKey readPublicKey() {
|
||||
public PublicKey readPublicKey()
|
||||
throws BufferException {
|
||||
try {
|
||||
switch (KeyType.fromString(readString())) {
|
||||
case RSA: {
|
||||
BigInteger e = readMPInt();
|
||||
BigInteger n = readMPInt();
|
||||
KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
|
||||
return keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
|
||||
}
|
||||
case DSA: {
|
||||
BigInteger p = readMPInt();
|
||||
BigInteger q = readMPInt();
|
||||
BigInteger g = readMPInt();
|
||||
BigInteger y = readMPInt();
|
||||
KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
|
||||
return keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
|
||||
}
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
final String type = readString();
|
||||
return KeyType.fromString(type).readPubKeyFromBuffer(type, this);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new SSHRuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T putPublicKey(PublicKey key) {
|
||||
final KeyType type = KeyType.fromKey(key);
|
||||
switch (type) {
|
||||
case RSA: {
|
||||
final RSAPublicKey rsaKey = (RSAPublicKey) key;
|
||||
putString(type.toString()) // ssh-rsa
|
||||
.putMPInt(rsaKey.getPublicExponent()) // e
|
||||
.putMPInt(rsaKey.getModulus()); // n
|
||||
break;
|
||||
}
|
||||
case DSA: {
|
||||
final DSAPublicKey dsaKey = (DSAPublicKey) key;
|
||||
putString(type.toString()) // ssh-dss
|
||||
.putMPInt(dsaKey.getParams().getP()) // p
|
||||
.putMPInt(dsaKey.getParams().getQ()) // q
|
||||
.putMPInt(dsaKey.getParams().getG()) // g
|
||||
.putMPInt(dsaKey.getY()); // y
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new SSHRuntimeException("Don't know how to encode key: " + key);
|
||||
}
|
||||
KeyType.fromKey(key).putPubKeyIntoBuffer(key, this);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,47 +15,241 @@
|
||||
*/
|
||||
package net.schmizz.sshj.common;
|
||||
|
||||
import com.hierynomus.sshj.secg.SecgUtils;
|
||||
import com.hierynomus.sshj.signature.Ed25519PublicKey;
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.math.GroupElement;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
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.interfaces.DSAPrivateKey;
|
||||
import java.security.interfaces.DSAPublicKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
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", new KeyChecker() {
|
||||
public boolean isMyType(Key key) {
|
||||
RSA("ssh-rsa") {
|
||||
@Override
|
||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
||||
throws GeneralSecurityException {
|
||||
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);
|
||||
}
|
||||
}),
|
||||
|
||||
},
|
||||
|
||||
/** SSH identifier for DSA keys */
|
||||
DSA("ssh-dss", new KeyChecker() {
|
||||
public boolean isMyType(Key key) {
|
||||
DSA("ssh-dss") {
|
||||
@Override
|
||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
||||
throws GeneralSecurityException {
|
||||
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;
|
||||
buf.putString(sType)
|
||||
.putMPInt(dsaKey.getParams().getP()) // p
|
||||
.putMPInt(dsaKey.getParams().getQ()) // q
|
||||
.putMPInt(dsaKey.getParams().getG()) // g
|
||||
.putMPInt(dsaKey.getY()); // y
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return (key instanceof DSAPublicKey || key instanceof DSAPrivateKey);
|
||||
}
|
||||
}),
|
||||
|
||||
},
|
||||
|
||||
/** 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);
|
||||
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;
|
||||
byte[] encoded = SecgUtils.getEncoded(ecdsa.getW(), ecdsa.getParams().getCurve());
|
||||
|
||||
buf.putString(sType)
|
||||
.putString(NISTP_CURVE)
|
||||
.putBytes(encoded);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return ("ECDSA".equals(key.getAlgorithm()));
|
||||
}
|
||||
},
|
||||
|
||||
ED25519("ssh-ed25519") {
|
||||
private final Logger logger = LoggerFactory.getLogger(KeyType.class);
|
||||
@Override
|
||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf) throws GeneralSecurityException {
|
||||
try {
|
||||
final int keyLen = buf.readUInt32AsInt();
|
||||
final byte[] p = new byte[keyLen];
|
||||
buf.readRawBytes(p);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Key algo: %s, Key curve: 25519, Key Len: %s\np: %s",
|
||||
type,
|
||||
keyLen,
|
||||
Arrays.toString(p))
|
||||
);
|
||||
}
|
||||
|
||||
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("ed25519-sha-512");
|
||||
GroupElement point = ed25519.getCurve().createPoint(p, true);
|
||||
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec(point, ed25519);
|
||||
return new Ed25519PublicKey(publicSpec);
|
||||
|
||||
} catch (Buffer.BufferException be) {
|
||||
throw new SSHRuntimeException(be);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
EdDSAPublicKey key = (EdDSAPublicKey) pk;
|
||||
buf.putString(sType).putBytes(key.getAbyte());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return "EdDSA".equals(key.getAlgorithm());
|
||||
}
|
||||
},
|
||||
|
||||
/** Unrecognized */
|
||||
UNKNOWN("unknown", null);
|
||||
UNKNOWN("unknown") {
|
||||
@Override
|
||||
public PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
||||
throws GeneralSecurityException {
|
||||
throw new UnsupportedOperationException("Don't know how to decode key:" + type);
|
||||
}
|
||||
|
||||
private static interface KeyChecker {
|
||||
boolean isMyType(Key key);
|
||||
}
|
||||
@Override
|
||||
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
|
||||
throw new UnsupportedOperationException("Don't know how to encode key: " + pk);
|
||||
}
|
||||
|
||||
private final String sType;
|
||||
private final KeyChecker checker;
|
||||
@Override
|
||||
protected boolean isMyType(Key key) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
private KeyType(String type, KeyChecker checker) {
|
||||
|
||||
private static final String NISTP_CURVE = "nistp256";
|
||||
|
||||
protected final String sType;
|
||||
|
||||
private KeyType(String type) {
|
||||
this.sType = type;
|
||||
this.checker = checker;
|
||||
}
|
||||
|
||||
public abstract PublicKey readPubKeyFromBuffer(String type, Buffer<?> buf)
|
||||
throws GeneralSecurityException;
|
||||
|
||||
public abstract void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
|
||||
|
||||
protected abstract boolean isMyType(Key key);
|
||||
|
||||
public static KeyType fromKey(Key key) {
|
||||
for (KeyType kt : values())
|
||||
if (kt.checker != null && kt.checker.isMyType((key)))
|
||||
if (kt.isMyType((key)))
|
||||
return kt;
|
||||
return UNKNOWN;
|
||||
}
|
||||
@@ -72,4 +266,4 @@ public enum KeyType {
|
||||
return sType;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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),
|
||||
@@ -29,7 +30,7 @@ public enum Message {
|
||||
|
||||
KEXDH_INIT(30),
|
||||
|
||||
/** { KEXDH_REPLY, KEXDH_GEX_GROUP } */
|
||||
/** { KEXDH_REPLY, KEXDH_GEX_GROUP, SSH_MSG_KEX_ECDH_REPLY } */
|
||||
KEXDH_31(31),
|
||||
|
||||
KEX_DH_GEX_INIT(32),
|
||||
@@ -45,6 +46,9 @@ public enum Message {
|
||||
USERAUTH_60(60),
|
||||
USERAUTH_INFO_RESPONSE(61),
|
||||
|
||||
USERAUTH_GSSAPI_EXCHANGE_COMPLETE(63),
|
||||
USERAUTH_GSSAPI_MIC(66),
|
||||
|
||||
GLOBAL_REQUEST(80),
|
||||
REQUEST_SUCCESS(81),
|
||||
REQUEST_FAILURE(82),
|
||||
@@ -67,8 +71,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) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
|
||||
@@ -48,6 +28,7 @@ public class SSHException
|
||||
|
||||
public static final ExceptionChainer<SSHException> chainer = new ExceptionChainer<SSHException>() {
|
||||
|
||||
@Override
|
||||
public SSHException chain(Throwable t) {
|
||||
if (t instanceof SSHException)
|
||||
return (SSHException) t;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,117 +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;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class StreamCopier
|
||||
extends Thread {
|
||||
public class StreamCopier {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(StreamCopier.class);
|
||||
public interface Listener {
|
||||
|
||||
void reportProgress(long transferred)
|
||||
throws IOException;
|
||||
|
||||
public interface ErrorCallback {
|
||||
void onError(IOException ioe);
|
||||
}
|
||||
|
||||
public static ErrorCallback closeOnErrorCallback(final Closeable... toClose) {
|
||||
final Closeable[] closeables = Arrays.copyOf(toClose, toClose.length);
|
||||
return new ErrorCallback() {
|
||||
public void onError(IOException ioe) {
|
||||
IOUtils.closeQuietly(closeables);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static long copy(InputStream in, OutputStream out, int bufSize, boolean keepFlushing)
|
||||
throws IOException {
|
||||
long count = 0;
|
||||
|
||||
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();
|
||||
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 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() {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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.
|
||||
@@ -24,6 +24,7 @@ public class ConnectionException
|
||||
extends SSHException {
|
||||
|
||||
public static final ExceptionChainer<ConnectionException> chainer = new ExceptionChainer<ConnectionException>() {
|
||||
@Override
|
||||
public ConnectionException chain(Throwable t) {
|
||||
if (t instanceof ConnectionException)
|
||||
return (ConnectionException) t;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,35 +51,47 @@ 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 trans transport layer
|
||||
* @param keepAlive the keep alive provider
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel get(int id) {
|
||||
return channels.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForwardedChannelOpener get(String chanType) {
|
||||
return openers.get(chanType);
|
||||
}
|
||||
|
||||
@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())
|
||||
@@ -85,26 +99,32 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,38 +154,31 @@ public class ConnectionImpl
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
public int getMaxPacketSize() {
|
||||
return maxPacketSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transport getTransport() {
|
||||
return trans;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxPacketSize(int maxPacketSize) {
|
||||
this.maxPacketSize = maxPacketSize;
|
||||
}
|
||||
|
||||
public int getWindowSize() {
|
||||
@Override
|
||||
public long getWindowSize() {
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
public void setWindowSize(int windowSize) {
|
||||
@Override
|
||||
public void setWindowSize(long windowSize) {
|
||||
this.windowSize = windowSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void join()
|
||||
throws InterruptedException {
|
||||
synchronized (internalSynchronizer) {
|
||||
@@ -174,68 +187,96 @@ public class ConnectionImpl
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextID() {
|
||||
return nextID.getAndIncrement();
|
||||
}
|
||||
|
||||
public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
|
||||
byte[] specifics)
|
||||
@Override
|
||||
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();
|
||||
if (gr == null)
|
||||
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"));
|
||||
else
|
||||
gr.set(response);
|
||||
"Got a global request response when none was requested");
|
||||
} else if (response == null) {
|
||||
gr.deliverError(new ConnectionException("Global request [" + gr + "] failed"));
|
||||
} else {
|
||||
// To prevent a race condition, copy the packet before delivering, as it will be handled in a different thread.
|
||||
gr.deliver(new SSHPacket(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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
@@ -78,16 +60,13 @@ public abstract class AbstractChannel
|
||||
|
||||
private final Queue<Event<ConnectionException>> chanReqResponseEvents = new LinkedList<Event<ConnectionException>>();
|
||||
|
||||
/* The lock used by #newEvent to create open & close events */
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
/* The lock used by to create the open & close events */
|
||||
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,62 +88,71 @@ 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
|
||||
public boolean getAutoExpand() {
|
||||
return autoExpand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalMaxPacketSize() {
|
||||
return lwin.getMaxPacketSize();
|
||||
}
|
||||
|
||||
public int getLocalWinSize() {
|
||||
@Override
|
||||
public long getLocalWinSize() {
|
||||
return lwin.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecipient() {
|
||||
return recipient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemoteMaxPacketSize() {
|
||||
return rwin.getMaxPacketSize();
|
||||
}
|
||||
|
||||
public int getRemoteWinSize() {
|
||||
@Override
|
||||
public long getRemoteWinSize() {
|
||||
return rwin.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Message msg, SSHPacket buf)
|
||||
throws ConnectionException, TransportException {
|
||||
switch (msg) {
|
||||
@@ -174,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:
|
||||
@@ -209,7 +197,7 @@ public abstract class AbstractChannel
|
||||
|
||||
private void gotClose()
|
||||
throws TransportException {
|
||||
log.info("Got close");
|
||||
log.debug("Got close");
|
||||
try {
|
||||
closeAllStreams();
|
||||
sendClose();
|
||||
@@ -223,82 +211,112 @@ public abstract class AbstractChannel
|
||||
IOUtils.closeQuietly(in, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoExpand(boolean autoExpand) {
|
||||
this.autoExpand = autoExpand;
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean isOpen() {
|
||||
lock.lock();
|
||||
@Override
|
||||
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)
|
||||
@@ -311,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. */
|
||||
@@ -371,25 +395,10 @@ public abstract class AbstractChannel
|
||||
in.eof();
|
||||
}
|
||||
|
||||
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 + " >";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,16 +32,16 @@ public interface Channel
|
||||
/** Direct channels are those that are initiated by us. */
|
||||
interface Direct
|
||||
extends Channel {
|
||||
|
||||
/**
|
||||
* Request opening this channel from remote end.
|
||||
*
|
||||
* @throws OpenFailException in case the channel open request was rejected
|
||||
* @throws net.schmizz.sshj.connection.ConnectionException
|
||||
* other connection-layer error
|
||||
* @throws TransportException error writing packets etc.
|
||||
* @throws OpenFailException in case the channel open request was rejected
|
||||
* @throws ConnectionException other connection-layer error
|
||||
* @throws TransportException error writing packets etc.
|
||||
*/
|
||||
void open()
|
||||
throws OpenFailException, ConnectionException, TransportException;
|
||||
throws ConnectionException, TransportException;
|
||||
|
||||
}
|
||||
|
||||
@@ -77,6 +78,7 @@ public interface Channel
|
||||
|
||||
|
||||
/** Close this channel. */
|
||||
@Override
|
||||
void close()
|
||||
throws TransportException, ConnectionException;
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
@@ -100,6 +77,7 @@ public final class ChannelInputStream
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void notifyError(SSHException error) {
|
||||
this.error = error;
|
||||
eof();
|
||||
@@ -109,7 +87,7 @@ public final class ChannelInputStream
|
||||
public int read()
|
||||
throws IOException {
|
||||
synchronized (b) {
|
||||
return read(b, 0, 1) == -1 ? -1 : b[0];
|
||||
return read(b, 0, 1) == -1 ? -1 : b[0] & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,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)
|
||||
@@ -152,7 +130,12 @@ public final class ChannelInputStream
|
||||
buf.putRawBytes(data, offset, len);
|
||||
buf.notifyAll();
|
||||
}
|
||||
win.consume(len);
|
||||
// Potential fix for #203 (window consumed below 0).
|
||||
// This seems to be a race condition if we receive more data, while we're already sending a SSH_MSG_CHANNEL_WINDOW_ADJUST
|
||||
// And the window has not expanded yet.
|
||||
synchronized (win) {
|
||||
win.consume(len);
|
||||
}
|
||||
if (chan.getAutoExpand())
|
||||
checkWindow();
|
||||
}
|
||||
@@ -160,11 +143,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);
|
||||
}
|
||||
}
|
||||
@@ -175,4 +158,4 @@ public final class ChannelInputStream
|
||||
return "< ChannelInputStream for Channel #" + chan.getID() + " >";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
@@ -54,28 +36,101 @@ public final class ChannelOutputStream
|
||||
implements ErrorNotifiable {
|
||||
|
||||
private final Channel chan;
|
||||
private Transport trans;
|
||||
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,75 +141,55 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void notifyError(SSHException error) {
|
||||
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
|
||||
@@ -162,4 +197,4 @@ public final class ChannelOutputStream
|
||||
return "< ChannelOutputStream for Channel #" + chan.getID() + " >";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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.
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C)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.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.hierynomus.sshj.backport.Sockets.asCloseable;
|
||||
|
||||
public class SocketStreamCopyMonitor
|
||||
extends Thread {
|
||||
|
||||
private SocketStreamCopyMonitor(Runnable r) {
|
||||
super(r);
|
||||
setName("sockmon");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
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, asCloseable(socket));
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,30 +17,28 @@ package net.schmizz.sshj.connection.channel;
|
||||
|
||||
import net.schmizz.sshj.common.SSHRuntimeException;
|
||||
import net.schmizz.sshj.connection.ConnectionException;
|
||||
import net.schmizz.sshj.transport.TransportException;
|
||||
import org.slf4j.Logger;
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -49,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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,19 +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()
|
||||
throws TransportException {
|
||||
public long neededAdjustment() {
|
||||
synchronized (lock) {
|
||||
return (size - threshold <= 0) ? (initialSize - size) : 0;
|
||||
return (size <= threshold) ? (initialSize - size) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
@@ -60,28 +41,39 @@ public abstract class AbstractDirectChannel
|
||||
conn.attach(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,119 +15,130 @@
|
||||
*/
|
||||
package net.schmizz.sshj.connection.channel.direct;
|
||||
|
||||
import net.schmizz.concurrent.Event;
|
||||
import net.schmizz.sshj.common.IOUtils;
|
||||
import net.schmizz.sshj.common.SSHPacket;
|
||||
import net.schmizz.sshj.common.StreamCopier;
|
||||
import net.schmizz.sshj.common.StreamCopier.ErrorCallback;
|
||||
import net.schmizz.sshj.connection.Connection;
|
||||
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.net.ServerSocketFactory;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.hierynomus.sshj.backport.Sockets.asCloseable;
|
||||
|
||||
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() {
|
||||
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()) //
|
||||
.bufSize(getRemoteMaxPacketSize()) //
|
||||
.errorCallback(closer) //
|
||||
.daemon(true) //
|
||||
.start();
|
||||
socket.setSendBufferSize(getLocalMaxPacketSize());
|
||||
socket.setReceiveBufferSize(getRemoteMaxPacketSize());
|
||||
final Event<IOException> soc2chan = new StreamCopier(socket.getInputStream(), getOutputStream())
|
||||
.bufSize(getRemoteMaxPacketSize())
|
||||
.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());
|
||||
return super.buildOpenReq()
|
||||
.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;
|
||||
|
||||
public LocalPortForwarder(Connection conn, SocketAddress listeningAddr, String host, int port)
|
||||
throws IOException {
|
||||
this(ServerSocketFactory.getDefault(), conn, listeningAddr, host, port);
|
||||
public LocalPortForwarder(Connection conn, Parameters parameters, ServerSocket serverSocket) {
|
||||
this.conn = conn;
|
||||
this.parameters = parameters;
|
||||
this.serverSocket = serverSocket;
|
||||
}
|
||||
|
||||
private void startChannel(Socket socket) throws IOException {
|
||||
DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
|
||||
try {
|
||||
chan.open();
|
||||
chan.start();
|
||||
} catch (IOException e) {
|
||||
IOUtils.closeQuietly(chan, asCloseable(socket));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a local port forwarder with specified binding ({@code listeningAddr}. It does not, however, start
|
||||
* listening unless {@link #listen() explicitly told to}.
|
||||
* Start listening for incoming connections and forward to remote host as a channel.
|
||||
*
|
||||
* @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}
|
||||
* @throws IOException
|
||||
*/
|
||||
public LocalPortForwarder(ServerSocketFactory ssf, Connection conn, SocketAddress listeningAddr, String host, int port)
|
||||
throws IOException {
|
||||
this.conn = conn;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.ss = ssf.createServerSocket();
|
||||
ss.setReceiveBufferSize(conn.getMaxPacketSize());
|
||||
ss.bind(listeningAddr);
|
||||
}
|
||||
|
||||
public SocketAddress getListeningAddress() {
|
||||
return ss.getLocalSocketAddress();
|
||||
}
|
||||
|
||||
/** Start listening for incoming connections and forward to remote host as a channel. */
|
||||
public void listen()
|
||||
throws IOException {
|
||||
log.info("Listening on {}", ss.getLocalSocketAddress());
|
||||
Socket sock;
|
||||
while (true) {
|
||||
sock = ss.accept();
|
||||
log.info("Got connection from {}", sock.getRemoteSocketAddress());
|
||||
DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, sock);
|
||||
chan.open();
|
||||
chan.start();
|
||||
log.info("Listening on {}", serverSocket.getLocalSocketAddress());
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
final Socket socket = serverSocket.accept();
|
||||
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
|
||||
startChannel(socket);
|
||||
}
|
||||
log.debug("Interrupted!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,34 +42,30 @@ 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();
|
||||
|
||||
/**
|
||||
* If the command exit violently {@link #getExitSignal() with a signal}, an error message would have been
|
||||
* received and can be retrieved via this method. Otherwise, this method will return {@code null}.
|
||||
* <p/>
|
||||
* <strong>NOTE: </strong> Always call {@link #close()} first before inspecting the exit error message.
|
||||
*/
|
||||
String getExitErrorMessage();
|
||||
|
||||
/**
|
||||
* Returns the {@link Signal signal} if the command exit violently, or {@code null} if this information was not
|
||||
* received.
|
||||
* <p/>
|
||||
* <strong>NOTE: </strong> Always call {@link #close()} first before inspecting the exit signal.
|
||||
*/
|
||||
Signal getExitSignal();
|
||||
|
||||
/**
|
||||
* Returns the exit status of the command if it was received, or {@code null} if this information was not
|
||||
* received.
|
||||
* <p/>
|
||||
* <strong>NOTE: </strong> Always call {@link #close()} first before inspecting the exit status.
|
||||
*/
|
||||
Integer getExitStatus();
|
||||
|
||||
@@ -81,16 +76,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 +131,7 @@ public interface Session
|
||||
/** Subsystem API. */
|
||||
interface Subsystem
|
||||
extends Channel {
|
||||
|
||||
Integer getExitStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,80 +12,53 @@
|
||||
* 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.HashMap;
|
||||
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 Integer exitStatus;
|
||||
private final ChannelInputStream err = new ChannelInputStream(this, trans, lwin);
|
||||
|
||||
private Signal exitSignal;
|
||||
private Boolean wasCoreDumped;
|
||||
private String exitErrMsg;
|
||||
private volatile Integer exitStatus;
|
||||
|
||||
private Boolean canDoFlowControl;
|
||||
private volatile Signal exitSignal;
|
||||
private volatile Boolean wasCoreDumped;
|
||||
private volatile String exitErrMsg;
|
||||
|
||||
private ChannelInputStream err = new ChannelInputStream(this, trans, lwin);
|
||||
private volatile Boolean canDoFlowControl;
|
||||
|
||||
private boolean usedUp;
|
||||
|
||||
public SessionChannel(Connection conn) {
|
||||
super(conn, "session");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void allocateDefaultPTY()
|
||||
throws ConnectionException, TransportException {
|
||||
// TODO FIXME (maybe?): These modes were originally copied from what SSHD was doing;
|
||||
// and then the echo modes were set to 0 to better serve the PTY example.
|
||||
// Not sure what default PTY modes should be.
|
||||
final Map<PTYMode, Integer> modes = new HashMap<PTYMode, Integer>();
|
||||
modes.put(PTYMode.ISIG, 1);
|
||||
modes.put(PTYMode.ICANON, 1);
|
||||
modes.put(PTYMode.ECHO, 0);
|
||||
modes.put(PTYMode.ECHOE, 0);
|
||||
modes.put(PTYMode.ECHOK, 0);
|
||||
modes.put(PTYMode.ECHONL, 0);
|
||||
modes.put(PTYMode.NOFLSH, 0);
|
||||
allocatePTY("vt100", 0, 0, 0, 0, modes);
|
||||
allocatePTY("vt100", 80, 24, 0, 0, Collections.<PTYMode, Integer>emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void allocatePTY(String term, int cols, int rows, int width, int height, Map<PTYMode, Integer> modes)
|
||||
throws ConnectionException, TransportException {
|
||||
sendChannelRequest(
|
||||
@@ -93,84 +66,87 @@ 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
|
||||
public Boolean canDoFlowControl() {
|
||||
return canDoFlowControl;
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
public String getErrorAsString()
|
||||
throws IOException {
|
||||
return StreamCopier.copyStreamToString(err);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getErrorStream() {
|
||||
return err;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExitErrorMessage() {
|
||||
return exitErrMsg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Signal getExitSignal() {
|
||||
return exitSignal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getExitStatus() {
|
||||
return exitStatus;
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -178,35 +154,44 @@ 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
|
||||
public void signal(Signal sig)
|
||||
throws TransportException {
|
||||
sendChannelRequest("signal", false, new Buffer.PlainBuffer().putString(sig.toString()));
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getExitWasCoreDumped() {
|
||||
return wasCoreDumped;
|
||||
}
|
||||
@@ -224,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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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,36 +34,41 @@ 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;
|
||||
init(recipient, remoteWinSize, remoteMaxPacketSize);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOriginatorIP() {
|
||||
return origIP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOriginatorPort() {
|
||||
return origPort;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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;
|
||||
@@ -40,19 +39,17 @@ public abstract class AbstractForwardedChannelOpener
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelType() {
|
||||
return chanType;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls the listener with the new channel in a separate thread.
|
||||
*/
|
||||
|
||||
/** Calls the listener with the new channel in a separate thread. */
|
||||
protected void callListener(final ConnectListener listener, final Channel.Forwarded chan) {
|
||||
new Thread() {
|
||||
|
||||
{
|
||||
setName("ConnectListener");
|
||||
setName("chanopener");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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 "" means that connections are to be accepted on all protocol
|
||||
* families supported by the SSH implementation.
|
||||
* <p/>
|
||||
*
|
||||
* o "0.0.0.0" means to listen on all IPv4 addresses.
|
||||
* <p/>
|
||||
*
|
||||
* o "::" means to listen on all IPv6 addresses.
|
||||
* <p/>
|
||||
*
|
||||
* o "localhost" means to listen on all protocol families supported by
|
||||
* the SSH implementation on loopback addresses only ([RFC3330] and
|
||||
* [RFC3513]).
|
||||
* <p/>
|
||||
*
|
||||
* o "127.0.0.1" and "::1" indicate listening on the loopback
|
||||
* interfaces for IPv4 and IPv6, respectively.
|
||||
* </pre>
|
||||
@@ -117,6 +117,34 @@ public class RemotePortForwarder
|
||||
return address + ":" + port;
|
||||
}
|
||||
|
||||
private boolean handles(ForwardedTCPIPChannel channel) {
|
||||
Forward channelForward = channel.getParentForward();
|
||||
if (channelForward.getPort() != port) {
|
||||
return false;
|
||||
}
|
||||
if ("".equals(address)) {
|
||||
// This forward handles all protocols
|
||||
return true;
|
||||
}
|
||||
if (channelForward.address.equals(address)) {
|
||||
// Addresses match up
|
||||
return true;
|
||||
}
|
||||
if ("localhost".equals(address) && (channelForward.address.equals("127.0.0.1") || channelForward.address.equals("::1"))) {
|
||||
// Localhost special case.
|
||||
return true;
|
||||
}
|
||||
if ("::".equals(address) && channelForward.address.indexOf("::") > 0) {
|
||||
// Listen on all IPv6
|
||||
return true;
|
||||
}
|
||||
if ("0.0.0.0".equals(address) && channelForward.address.indexOf('.') > 0) {
|
||||
// Listen on all IPv4
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A {@code forwarded-tcpip} channel. */
|
||||
@@ -127,14 +155,14 @@ public class RemotePortForwarder
|
||||
|
||||
private final Forward fwd;
|
||||
|
||||
public ForwardedTCPIPChannel(Connection conn, int recipient, int remoteWinSize, int remoteMaxPacketSize,
|
||||
Forward fwd, String origIP, int origPort)
|
||||
throws TransportException {
|
||||
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;
|
||||
}
|
||||
|
||||
/** Returns the forwarding from which this channel originates. */
|
||||
/** @return the forwarding from which this channel originates. */
|
||||
public Forward getParentForward() {
|
||||
return fwd;
|
||||
}
|
||||
@@ -169,7 +197,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;
|
||||
@@ -194,10 +226,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. */
|
||||
@@ -209,16 +241,26 @@ public class RemotePortForwarder
|
||||
* Internal API. Creates a {@link ForwardedTCPIPChannel} from the {@code CHANNEL_OPEN} request and calls associated
|
||||
* {@code ConnectListener} for that forward in a separate thread.
|
||||
*/
|
||||
@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());
|
||||
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() + "`");
|
||||
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);
|
||||
}
|
||||
|
||||
for (Forward forward : listeners.keySet()) {
|
||||
if (forward.handles(chan)) {
|
||||
callListener(listeners.get(forward), chan);
|
||||
return;
|
||||
}
|
||||
}
|
||||
chan.reject(OpenFailException.Reason.ADMINISTRATIVELY_PROHIBITED, "Forwarding was not requested on `"
|
||||
+ chan.getParentForward() + "`");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 Shikhar Bhushan
|
||||
* Copyright (C)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
|
||||
@@ -40,9 +41,10 @@ public class SocketForwardingConnectListener
|
||||
}
|
||||
|
||||
/** On connect, confirm the channel and start forwarding. */
|
||||
@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());
|
||||
@@ -53,24 +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() {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user