View Javadoc
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
58       * from the target host and port.
59       * </p>
60       * <p>
61       * The command will be created for each host/port pair by using {@linkplain String#format(String, Object...)} with
62       * two objects: the target host name ({@linkplain String}) and the target port ({@linkplain Integer}).
63       * </p>
64       * <p>
65       * <p>
66       * Here are two examples (that can be easily used by using the static members of this class):
67       * </p>
68       * <ul>
69       * <li>{@code nc -q 0 %s %d} to use the netcat command ({@linkplain #NETCAT_COMMAND})</li>
70       * <li>{@code /bin/bash -c 'exec 3<>/dev/tcp/%s/%d; cat <&3 & cat >&3; kill $!} will use bash built-in TCP
71       * stream, which can be useful when there is no netcat available. ({@linkplain #BASH_TCP_COMMAND})</li>
72       * </ul>
73       */
74      private final String commandFormat;
75  
76      /**
77       * Hostname used to connect to the proxy host.
78       */
79      private final String proxyHost;
80  
81      /**
82       * The options for connection.
83       */
84      private final FileSystemOptions proxyOptions;
85  
86      /**
87       * The password to be used for connection.
88       */
89      private final String proxyPassword;
90  
91      /**
92       * Port used to connect to the proxy host.
93       */
94      private final int proxyPort;
95  
96      /**
97       * Username used to connect to the proxy host.
98       */
99      private final String proxyUser;
100 
101     private Session session;
102 
103     /**
104      * Creates a stream proxy.
105      *
106      * @param commandFormat A format string that will be used to create the command to execute on the proxy host using
107      *            {@linkplain String#format(String, Object...)}. Two parameters are given to the format command, the
108      *            target host name (String) and port (Integer).
109      *
110      * @param proxyUser The proxy user
111      * @param proxyPassword The proxy password
112      * @param proxyHost The proxy host
113      * @param proxyPort The port to connect to on the proxy
114      * @param proxyOptions Options used when connecting to the proxy
115      */
116     public SftpStreamProxy(final String commandFormat, final String proxyUser, final String proxyHost,
117             final int proxyPort, final String proxyPassword, final FileSystemOptions proxyOptions) {
118         this.proxyHost = proxyHost;
119         this.proxyPort = proxyPort;
120         this.proxyUser = proxyUser;
121         this.proxyPassword = proxyPassword;
122         this.commandFormat = commandFormat;
123         this.proxyOptions = proxyOptions;
124     }
125 
126     @Override
127     public void close() {
128         if (channel != null) {
129             channel.disconnect();
130         }
131         if (session != null) {
132             session.disconnect();
133         }
134     }
135 
136     @Override
137     public void connect(final SocketFactory socketFactory, final String targetHost, final int targetPort,
138             final int timeout) throws Exception {
139         session = SftpClientFactory.createConnection(proxyHost, proxyPort, proxyUser.toCharArray(),
140                 proxyPassword.toCharArray(), proxyOptions);
141         channel = (ChannelExec) session.openChannel("exec");
142         channel.setCommand(String.format(commandFormat, targetHost, targetPort));
143         channel.connect(timeout);
144     }
145 
146     @Override
147     public InputStream getInputStream() {
148         try {
149             return channel.getInputStream();
150         } catch (final IOException e) {
151             throw new IllegalStateException("IOException getting the SSH proxy input stream", e);
152         }
153     }
154 
155     @Override
156     public OutputStream getOutputStream() {
157         try {
158             return channel.getOutputStream();
159         } catch (final IOException e) {
160             throw new IllegalStateException("IOException getting the SSH proxy output stream", e);
161         }
162     }
163 
164     @Override
165     public Socket getSocket() {
166         return null;
167     }
168 }