aboutsummaryrefslogtreecommitdiffstats
path: root/sshlib/src/main/java/com/trilead/ssh2/HTTPProxyData.java
blob: 3c3c54a0cfe18294a6ec06f9d1a525f009e136da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package com.trilead.ssh2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

import com.trilead.ssh2.crypto.Base64;
import com.trilead.ssh2.transport.ClientServerHello;

/**
 * A <code>HTTPProxyData</code> object is used to specify the needed connection data
 * to connect through a HTTP proxy. 
 * 
 * @see Connection#setProxyData(ProxyData)
 * 
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: HTTPProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
 */

public class HTTPProxyData implements ProxyData
{
	private final String proxyHost;
	private final int proxyPort;
	private final String proxyUser;
	private final String proxyPass;
	private final String[] requestHeaderLines;

	/**
	 * Same as calling {@link #HTTPProxyData(String, int, String, String) HTTPProxyData(proxyHost, proxyPort, <code>null</code>, <code>null</code>)}
	 * 
	 * @param proxyHost Proxy hostname.
	 * @param proxyPort Proxy port.
	 */
	public HTTPProxyData(String proxyHost, int proxyPort)
	{
		this(proxyHost, proxyPort, null, null);
	}

	/**
	 * Same as calling {@link #HTTPProxyData(String, int, String, String, String[]) HTTPProxyData(proxyHost, proxyPort, <code>null</code>, <code>null</code>, <code>null</code>)}
	 *
	 * @param proxyHost Proxy hostname.
	 * @param proxyPort Proxy port.
	 * @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
	 * @param proxyPass Password for basic authentication (<code>null</code> if no authentication is needed).
	 */
	public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass)
	{
		this(proxyHost, proxyPort, proxyUser, proxyPass, null);
	}

	/**
	 * Connection data for a HTTP proxy. It is possible to specify a username and password
	 * if the proxy requires basic authentication. Also, additional request header lines can
	 * be specified (e.g., "User-Agent: CERN-LineMode/2.15 libwww/2.17b3").
	 * <p>
	 * Please note: if you want to use basic authentication, then both <code>proxyUser</code>
	 * and <code>proxyPass</code> must be non-null.
	 * <p>
	 * Here is an example:
	 * <p>
	 * <code>
	 * new HTTPProxyData("192.168.1.1", "3128", "proxyuser", "secret", new String[] {"User-Agent: TrileadBasedClient/1.0", "X-My-Proxy-Option: something"});
	 * </code>
	 * 
	 * @param proxyHost Proxy hostname.
	 * @param proxyPort Proxy port.
	 * @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
	 * @param proxyPass Password for basic authentication (<code>null</code> if no authentication is needed).
	 * @param requestHeaderLines An array with additional request header lines (without end-of-line markers)
	 *        that have to be sent to the server. May be <code>null</code>.
	 */

	public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass,
			String[] requestHeaderLines)
	{
		if (proxyHost == null)
			throw new IllegalArgumentException("proxyHost must be non-null");

		if (proxyPort < 0)
			throw new IllegalArgumentException("proxyPort must be non-negative");

		this.proxyHost = proxyHost;
		this.proxyPort = proxyPort;
		this.proxyUser = proxyUser;
		this.proxyPass = proxyPass;
		this.requestHeaderLines = requestHeaderLines;
	}

	@Override
	public Socket openConnection(String hostname, int port, int connectTimeout) throws IOException {
		Socket sock = new Socket();

		InetAddress addr = InetAddress.getByName(proxyHost);
		sock.connect(new InetSocketAddress(addr, proxyPort), connectTimeout);
		sock.setSoTimeout(0);

			/* OK, now tell the proxy where we actually want to connect to */

		StringBuffer sb = new StringBuffer();

		sb.append("CONNECT ");
		sb.append(hostname);
		sb.append(':');
		sb.append(port);
		sb.append(" HTTP/1.0\r\n");

		if ((proxyUser != null) && (proxyPass != null))
		{
			String credentials = proxyUser + ":" + proxyPass;
			char[] encoded = Base64.encode(credentials.getBytes("ISO-8859-1"));
			sb.append("Proxy-Authorization: Basic ");
			sb.append(encoded);
			sb.append("\r\n");
		}

		if (requestHeaderLines != null)
		{
			for (int i = 0; i < requestHeaderLines.length; i++)
			{
				if (requestHeaderLines[i] != null)
				{
					sb.append(requestHeaderLines[i]);
					sb.append("\r\n");
				}
			}
		}

		sb.append("\r\n");

		OutputStream out = sock.getOutputStream();

		out.write(sb.toString().getBytes("ISO-8859-1"));
		out.flush();

			/* Now parse the HTTP response */

		byte[] buffer = new byte[1024];
		InputStream in = sock.getInputStream();

		int len = ClientServerHello.readLineRN(in, buffer);

		String httpReponse = new String(buffer, 0, len, "ISO-8859-1");

		if (httpReponse.startsWith("HTTP/") == false)
			throw new IOException("The proxy did not send back a valid HTTP response.");

			/* "HTTP/1.X XYZ X" => 14 characters minimum */

		if ((httpReponse.length() < 14) || (httpReponse.charAt(8) != ' ') || (httpReponse.charAt(12) != ' '))
			throw new IOException("The proxy did not send back a valid HTTP response.");

		int errorCode = 0;

		try
		{
			errorCode = Integer.parseInt(httpReponse.substring(9, 12));
		}
		catch (NumberFormatException ignore)
		{
			throw new IOException("The proxy did not send back a valid HTTP response.");
		}

		if ((errorCode < 0) || (errorCode > 999))
			throw new IOException("The proxy did not send back a valid HTTP response.");

		if (errorCode != 200)
		{
			throw new HTTPProxyException(httpReponse.substring(13), errorCode);
		}

			/* OK, read until empty line */

		while (true)
		{
			len = ClientServerHello.readLineRN(in, buffer);
			if (len == 0)
				break;
		}

		return sock;
	}
}