1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.net.ftp;
19
20 import java.io.BufferedReader;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.OutputStream;
25 import java.io.UnsupportedEncodingException;
26 import java.net.Inet6Address;
27 import java.net.Socket;
28 import java.net.SocketException;
29 import java.util.ArrayList;
30 import java.util.List;
31
32 import org.apache.commons.net.util.Base64;
33
34
35
36
37
38
39 public class FTPHTTPClient extends FTPClient {
40 private final String proxyHost;
41 private final int proxyPort;
42 private final String proxyUsername;
43 private final String proxyPassword;
44
45 private static final byte[] CRLF={'\r', '\n'};
46 private final Base64 base64 = new Base64();
47
48 private String tunnelHost;
49
50 public FTPHTTPClient(String proxyHost, int proxyPort, String proxyUser, String proxyPass) {
51 this.proxyHost = proxyHost;
52 this.proxyPort = proxyPort;
53 this.proxyUsername = proxyUser;
54 this.proxyPassword = proxyPass;
55 this.tunnelHost = null;
56 }
57
58 public FTPHTTPClient(String proxyHost, int proxyPort) {
59 this(proxyHost, proxyPort, null, null);
60 }
61
62
63
64
65
66
67
68
69
70
71 @Override
72 @Deprecated
73 protected Socket _openDataConnection_(int command, String arg)
74 throws IOException {
75 return super._openDataConnection_(command, arg);
76 }
77
78
79
80
81
82
83
84 @Override
85 protected Socket _openDataConnection_(String command, String arg)
86 throws IOException {
87
88 if (getDataConnectionMode() != PASSIVE_LOCAL_DATA_CONNECTION_MODE) {
89 throw new IllegalStateException("Only passive connection mode supported");
90 }
91
92 final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address;
93 String passiveHost = null;
94
95 boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address;
96 if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) {
97 _parseExtendedPassiveModeReply(_replyLines.get(0));
98 passiveHost = this.tunnelHost;
99 } else {
100 if (isInet6Address) {
101 return null;
102 }
103
104 if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) {
105 return null;
106 }
107 _parsePassiveModeReply(_replyLines.get(0));
108 passiveHost = this.getPassiveHost();
109 }
110
111 Socket socket = new Socket(proxyHost, proxyPort);
112 InputStream is = socket.getInputStream();
113 OutputStream os = socket.getOutputStream();
114 tunnelHandshake(passiveHost, this.getPassivePort(), is, os);
115 if ((getRestartOffset() > 0) && !restart(getRestartOffset())) {
116 socket.close();
117 return null;
118 }
119
120 if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) {
121 socket.close();
122 return null;
123 }
124
125 return socket;
126 }
127
128 @Override
129 public void connect(String host, int port) throws SocketException, IOException {
130
131 _socket_ = new Socket(proxyHost, proxyPort);
132 _input_ = _socket_.getInputStream();
133 _output_ = _socket_.getOutputStream();
134 try {
135 tunnelHandshake(host, port, _input_, _output_);
136 }
137 catch (Exception e) {
138 IOException ioe = new IOException("Could not connect to " + host+ " using port " + port);
139 ioe.initCause(e);
140 throw ioe;
141 }
142 super._connectAction_();
143 }
144
145 private void tunnelHandshake(String host, int port, InputStream input, OutputStream output) throws IOException,
146 UnsupportedEncodingException {
147 final String connectString = "CONNECT " + host + ":" + port + " HTTP/1.1";
148 final String hostString = "Host: " + host + ":" + port;
149
150 this.tunnelHost = host;
151 output.write(connectString.getBytes("UTF-8"));
152 output.write(CRLF);
153 output.write(hostString.getBytes("UTF-8"));
154 output.write(CRLF);
155
156 if (proxyUsername != null && proxyPassword != null) {
157 final String auth = proxyUsername + ":" + proxyPassword;
158 final String header = "Proxy-Authorization: Basic "
159 + base64.encodeToString(auth.getBytes("UTF-8"));
160 output.write(header.getBytes("UTF-8"));
161 }
162 output.write(CRLF);
163
164 List<String> response = new ArrayList<String>();
165 BufferedReader reader = new BufferedReader(
166 new InputStreamReader(input, getCharsetName()));
167
168 for (String line = reader.readLine(); line != null
169 && line.length() > 0; line = reader.readLine()) {
170 response.add(line);
171 }
172
173 int size = response.size();
174 if (size == 0) {
175 throw new IOException("No response from proxy");
176 }
177
178 String code = null;
179 String resp = response.get(0);
180 if (resp.startsWith("HTTP/") && resp.length() >= 12) {
181 code = resp.substring(9, 12);
182 } else {
183 throw new IOException("Invalid response from proxy: " + resp);
184 }
185
186 if (!"200".equals(code)) {
187 StringBuilder msg = new StringBuilder();
188 msg.append("HTTPTunnelConnector: connection failed\r\n");
189 msg.append("Response received from the proxy:\r\n");
190 for (String line : response) {
191 msg.append(line);
192 msg.append("\r\n");
193 }
194 throw new IOException(msg.toString());
195 }
196 }
197 }
198
199