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