001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.vfs2.provider.sftp; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.OutputStream; 023import java.net.Socket; 024 025import org.apache.commons.vfs2.FileSystemOptions; 026 027import com.jcraft.jsch.ChannelExec; 028import com.jcraft.jsch.Proxy; 029import com.jcraft.jsch.Session; 030import com.jcraft.jsch.SocketFactory; 031 032/** 033 * Stream based proxy for JSch. 034 * 035 * <p> 036 * Use a command on the proxy that will forward the SSH stream to the target host and port. 037 * </p> 038 * 039 * @since 2.1 040 */ 041public class SftpStreamProxy implements Proxy { 042 /** 043 * Command format using bash built-in TCP stream. 044 */ 045 public static final String BASH_TCP_COMMAND = "/bin/bash -c 'exec 3<>/dev/tcp/%s/%d; cat <&3 & cat >&3; kill $!"; 046 047 /** 048 * Command format using netcat command. 049 */ 050 public static final String NETCAT_COMMAND = "nc -q 0 %s %d"; 051 052 private ChannelExec channel; 053 054 /** 055 * Command pattern to execute on the proxy host. 056 * <p> 057 * When run, the command output should be forwarded to the target host and port, and its input should be forwarded 058 * from the target host and port. 059 * <p> 060 * The command will be created for each host/port pair by using {@linkplain String#format(String, Object...)} with 061 * two objects: the target host name ({@linkplain String}) and the target port ({@linkplain Integer}). 062 * <p> 063 * Here are two examples (that can be easily used by using the static members of this class): 064 * <ul> 065 * <li><code>nc -q 0 %s %d</code> to use the netcat command ({@linkplain #NETCAT_COMMAND})</li> 066 * <li><code>/bin/bash -c 'exec 3<>/dev/tcp/%s/%d; cat <&3 & cat >&3; kill $!</code> will use bash built-in TCP 067 * stream, which can be useful when there is no netcat available. ({@linkplain #BASH_TCP_COMMAND})</li> 068 * </ul> 069 */ 070 private final String commandFormat; 071 072 /** 073 * Hostname used to connect to the proxy host. 074 */ 075 private final String proxyHost; 076 077 /** 078 * The options for connection. 079 */ 080 private final FileSystemOptions proxyOptions; 081 082 /** 083 * The password to be used for connection. 084 */ 085 private final String proxyPassword; 086 087 /** 088 * Port used to connect to the proxy host. 089 */ 090 private final int proxyPort; 091 092 /** 093 * Username used to connect to the proxy host. 094 */ 095 private final String proxyUser; 096 097 private Session session; 098 099 /** 100 * Creates a stream proxy. 101 * 102 * @param commandFormat A format string that will be used to create the command to execute on the proxy host using 103 * {@linkplain String#format(String, Object...)}. Two parameters are given to the format command, the 104 * target host name (String) and port (Integer). 105 * @param proxyUser The proxy user 106 * @param proxyPassword The proxy password 107 * @param proxyHost The proxy host 108 * @param proxyPort The port to connect to on the proxy 109 * @param proxyOptions Options used when connecting to the proxy 110 */ 111 public SftpStreamProxy(final String commandFormat, final String proxyUser, final String proxyHost, 112 final int proxyPort, final String proxyPassword, final FileSystemOptions proxyOptions) { 113 this.proxyHost = proxyHost; 114 this.proxyPort = proxyPort; 115 this.proxyUser = proxyUser; 116 this.proxyPassword = proxyPassword; 117 this.commandFormat = commandFormat; 118 this.proxyOptions = proxyOptions; 119 } 120 121 @Override 122 public void close() { 123 if (channel != null) { 124 channel.disconnect(); 125 } 126 if (session != null) { 127 session.disconnect(); 128 } 129 } 130 131 @Override 132 public void connect(final SocketFactory socketFactory, final String targetHost, final int targetPort, 133 final int timeout) throws Exception { 134 session = SftpClientFactory.createConnection(proxyHost, proxyPort, proxyUser.toCharArray(), 135 proxyPassword.toCharArray(), proxyOptions); 136 channel = (ChannelExec) session.openChannel("exec"); 137 channel.setCommand(String.format(commandFormat, targetHost, targetPort)); 138 channel.connect(timeout); 139 } 140 141 @Override 142 public InputStream getInputStream() { 143 try { 144 return channel.getInputStream(); 145 } catch (final IOException e) { 146 throw new IllegalStateException("IOException getting the SSH proxy input stream", e); 147 } 148 } 149 150 @Override 151 public OutputStream getOutputStream() { 152 try { 153 return channel.getOutputStream(); 154 } catch (final IOException e) { 155 throw new IllegalStateException("IOException getting the SSH proxy output stream", e); 156 } 157 } 158 159 @Override 160 public Socket getSocket() { 161 return null; 162 } 163}