1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.vfs2.provider.sftp;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.net.Socket;
24
25 import org.apache.commons.vfs2.FileSystemOptions;
26
27 import com.jcraft.jsch.ChannelExec;
28 import com.jcraft.jsch.Proxy;
29 import com.jcraft.jsch.Session;
30 import com.jcraft.jsch.SocketFactory;
31
32 /**
33 * Stream based proxy for JSch.
34 *
35 * <p>
36 * Use a command on the proxy that will forward the SSH stream to the target host and port.
37 * </p>
38 *
39 * @since 2.1
40 */
41 public class SftpStreamProxy implements Proxy {
42 /**
43 * Command format using bash built-in TCP stream.
44 */
45 public static final String BASH_TCP_COMMAND = "/bin/bash -c 'exec 3<>/dev/tcp/%s/%d; cat <&3 & cat >&3; kill $!";
46
47 /**
48 * Command format using netcat command.
49 */
50 public static final String NETCAT_COMMAND = "nc -q 0 %s %d";
51
52 private ChannelExec channel;
53
54 /**
55 * Command pattern to execute on the proxy host.
56 * <p>
57 * When run, the command output should be forwarded to the target host and port, and its input should be forwarded from the target host and port.
58 * </p>
59 * <p>
60 * The command will be created for each host/port pair by using {@linkplain String#format(String, Object...)} with two objects: the target host name
61 * ({@linkplain String}) and the target port ({@linkplain Integer}).
62 * </p>
63 * <p>
64 * Here are two examples (that can be easily used by using the static members of this class):
65 * </p>
66 * <ul>
67 * <li>{@code nc -q 0 %s %d} to use the netcat command ({@linkplain #NETCAT_COMMAND})</li>
68 * <li>{@code /bin/bash -c 'exec 3<>/dev/tcp/%s/%d; cat <&3 & cat >&3; kill $!} will use bash built-in TCP stream, which can be useful when there is no
69 * netcat available. ({@linkplain #BASH_TCP_COMMAND})</li>
70 * </ul>
71 */
72 private final String commandFormat;
73
74 /**
75 * Hostname used to connect to the proxy host.
76 */
77 private final String proxyHost;
78
79 /**
80 * The options for connection.
81 */
82 private final FileSystemOptions proxyOptions;
83
84 /**
85 * The password to be used for connection.
86 */
87 private final String proxyPassword;
88
89 /**
90 * Port used to connect to the proxy host.
91 */
92 private final int proxyPort;
93
94 /**
95 * User name used to connect to the proxy host.
96 */
97 private final String proxyUser;
98
99 private Session session;
100
101 /**
102 * Creates a stream proxy.
103 *
104 * @param commandFormat A format string that will be used to create the command to execute on the proxy host using
105 * {@linkplain String#format(String, Object...)}. Two parameters are given to the format command, the
106 * target host name (String) and port (Integer).
107 *
108 * @param proxyUser The proxy user
109 * @param proxyPassword The proxy password
110 * @param proxyHost The proxy host
111 * @param proxyPort The port to connect to on the proxy
112 * @param proxyOptions Options used when connecting to the proxy
113 */
114 public SftpStreamProxy(final String commandFormat, final String proxyUser, final String proxyHost,
115 final int proxyPort, final String proxyPassword, final FileSystemOptions proxyOptions) {
116 this.proxyHost = proxyHost;
117 this.proxyPort = proxyPort;
118 this.proxyUser = proxyUser;
119 this.proxyPassword = proxyPassword;
120 this.commandFormat = commandFormat;
121 this.proxyOptions = proxyOptions;
122 }
123
124 @Override
125 public void close() {
126 if (channel != null) {
127 channel.disconnect();
128 }
129 if (session != null) {
130 session.disconnect();
131 }
132 }
133
134 @Override
135 public void connect(final SocketFactory socketFactory, final String targetHost, final int targetPort,
136 final int timeout) throws Exception {
137 session = SftpClientFactory.createConnection(proxyHost, proxyPort, proxyUser.toCharArray(),
138 proxyPassword.toCharArray(), proxyOptions);
139 channel = (ChannelExec) session.openChannel("exec");
140 channel.setCommand(String.format(commandFormat, targetHost, targetPort));
141 channel.connect(timeout);
142 }
143
144 @Override
145 public InputStream getInputStream() {
146 try {
147 return channel.getInputStream();
148 } catch (final IOException e) {
149 throw new IllegalStateException("IOException getting the SSH proxy input stream", e);
150 }
151 }
152
153 @Override
154 public OutputStream getOutputStream() {
155 try {
156 return channel.getOutputStream();
157 } catch (final IOException e) {
158 throw new IllegalStateException("IOException getting the SSH proxy output stream", e);
159 }
160 }
161
162 @Override
163 public Socket getSocket() {
164 return null;
165 }
166 }