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 from the target host and port. 058 * </p> 059 * <p> 060 * The command will be created for each host/port pair by using {@linkplain String#format(String, Object...)} with two objects: the target host name 061 * ({@linkplain String}) and the target port ({@linkplain Integer}). 062 * </p> 063 * <p> 064 * Here are two examples (that can be easily used by using the static members of this class): 065 * </p> 066 * <ul> 067 * <li>{@code nc -q 0 %s %d} to use the netcat command ({@linkplain #NETCAT_COMMAND})</li> 068 * <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 069 * netcat available. ({@linkplain #BASH_TCP_COMMAND})</li> 070 * </ul> 071 */ 072 private final String commandFormat; 073 074 /** 075 * Hostname used to connect to the proxy host. 076 */ 077 private final String proxyHost; 078 079 /** 080 * The options for connection. 081 */ 082 private final FileSystemOptions proxyOptions; 083 084 /** 085 * The password to be used for connection. 086 */ 087 private final String proxyPassword; 088 089 /** 090 * Port used to connect to the proxy host. 091 */ 092 private final int proxyPort; 093 094 /** 095 * User name used to connect to the proxy host. 096 */ 097 private final String proxyUser; 098 099 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}