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 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 }