Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/test/jdk/sun/net/www/protocol/http/NTLMHeadTest.java
66646 views
1
/*
2
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*/
23
24
/*
25
* @test
26
* @bug 8270290
27
* @modules java.base/sun.net.www
28
* @library /test/lib
29
* @run main/othervm NTLMHeadTest SERVER
30
* @run main/othervm NTLMHeadTest PROXY
31
* @run main/othervm NTLMHeadTest TUNNEL
32
* @summary test for the incorrect logic in reading (and discarding) HTTP
33
* response body when processing NTLMSSP_CHALLENGE response
34
* (to CONNECT request) from proxy server. When this response is received
35
* by client, reset() is called on the connection to read and discard the
36
* response body. This code path was broken when initial client request
37
* uses HEAD method and HTTPS resource, in this case CONNECT is sent to
38
* proxy server (to establish TLS tunnel) and response body is not read
39
* from a socket (because initial method on client connection is HEAD).
40
* This does not cause problems with the majority of proxy servers because
41
* InputStream opened over the response socket is buffered with 8kb buffer
42
* size. Problem is only reproducible if the response size (headers +
43
* body) is larger than 8kb. The code path with HTTPS tunneling is checked
44
* with TUNNEL argument. Additional checks for HEAD handling are included
45
* for direct server (SERVER) and HTTP proxying (PROXY) code paths, in
46
* these (non-tunnel) cases client must NOT attempt to read response data
47
* (to not block on socket read) because HEAD is sent to server and
48
* NTLMSSP_CHALLENGE response includes Content-Length, but does not
49
* include the body.
50
*/
51
52
import java.net.*;
53
import java.io.*;
54
import java.util.*;
55
import sun.net.www.MessageHeader;
56
import jdk.test.lib.net.URIBuilder;
57
58
public class NTLMHeadTest {
59
60
enum Mode { SERVER, PROXY, TUNNEL }
61
62
static final int BODY_LEN = 8192;
63
64
static final String RESP_SERVER_AUTH =
65
"HTTP/1.1 401 Unauthorized\r\n" +
66
"WWW-Authenticate: NTLM\r\n" +
67
"Connection: close\r\n" +
68
"Content-Length: " + BODY_LEN + "\r\n" +
69
"\r\n";
70
71
static final String RESP_SERVER_NTLM =
72
"HTTP/1.1 401 Unauthorized\r\n" +
73
"WWW-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAACgAAAABggAAU3J2Tm9uY2UAAAAAAAAAAA==\r\n" +
74
"Connection: Keep-Alive\r\n" +
75
"Content-Length: " + BODY_LEN + "\r\n" +
76
"\r\n";
77
78
static final String RESP_SERVER_OR_PROXY_DEST =
79
"HTTP/1.1 200 OK\r\n" +
80
"Connection: close\r\n" +
81
"Content-Length: 42\r\n" +
82
"\r\n";
83
84
static final String RESP_PROXY_AUTH =
85
"HTTP/1.1 407 Proxy Authentication Required\r\n" +
86
"Proxy-Authenticate: NTLM\r\n" +
87
"Proxy-Connection: close\r\n" +
88
"Connection: close\r\n" +
89
"Content-Length: " + BODY_LEN + "\r\n" +
90
"\r\n";
91
92
static final String RESP_PROXY_NTLM =
93
"HTTP/1.1 407 Proxy Authentication Required\r\n" +
94
"Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAACgAAAABggAAU3J2Tm9uY2UAAAAAAAAAAA==\r\n" +
95
"Proxy-Connection: Keep-Alive\r\n" +
96
"Connection: Keep-Alive\r\n" +
97
"Content-Length: " + BODY_LEN + "\r\n" +
98
"\r\n";
99
100
static final String RESP_TUNNEL_AUTH =
101
"HTTP/1.1 407 Proxy Authentication Required\r\n" +
102
"Proxy-Authenticate: NTLM\r\n" +
103
"Proxy-Connection: close\r\n" +
104
"Connection: close\r\n" +
105
"Content-Length: " + BODY_LEN + "\r\n" +
106
"\r\n" +
107
generateBody(BODY_LEN);
108
109
static final String RESP_TUNNEL_NTLM =
110
"HTTP/1.1 407 Proxy Authentication Required\r\n" +
111
"Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAACgAAAABggAAU3J2Tm9uY2UAAAAAAAAAAA==\r\n" +
112
"Proxy-Connection: Keep-Alive\r\n" +
113
"Connection: Keep-Alive\r\n" +
114
"Content-Length: " + BODY_LEN + "\r\n" +
115
"\r\n" +
116
generateBody(BODY_LEN);
117
118
static final String RESP_TUNNEL_ESTABLISHED =
119
"HTTP/1.1 200 Connection Established\r\n\r\n";
120
121
public static void main(String[] args) throws Exception {
122
Authenticator.setDefault(new TestAuthenticator());
123
if (1 != args.length) {
124
throw new IllegalArgumentException("Mode value must be specified, one of: [SERVER, PROXY, TUNNEL]");
125
}
126
Mode mode = Mode.valueOf(args[0]);
127
System.out.println("Running with mode: " + mode);
128
switch (mode) {
129
case SERVER: testSever(); return;
130
case PROXY: testProxy(); return;
131
case TUNNEL: testTunnel(); return;
132
default: throw new IllegalArgumentException("Invalid mode: " + mode);
133
}
134
}
135
136
static void testSever() throws Exception {
137
try (NTLMServer server = startServer(new ServerSocket(0, 0, InetAddress.getLoopbackAddress()), Mode.SERVER)) {
138
URL url = URIBuilder.newBuilder()
139
.scheme("http")
140
.loopback()
141
.port(server.getLocalPort())
142
.path("/")
143
.toURLUnchecked();
144
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
145
uc.setRequestMethod("HEAD");
146
uc.getInputStream().readAllBytes();
147
}
148
}
149
150
static void testProxy() throws Exception {
151
InetAddress loopback = InetAddress.getLoopbackAddress();
152
try (NTLMServer server = startServer(new ServerSocket(0, 0, loopback), Mode.PROXY)) {
153
SocketAddress proxyAddr = new InetSocketAddress(loopback, server.getLocalPort());
154
Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP, proxyAddr);
155
URL url = URIBuilder.newBuilder()
156
.scheme("http")
157
.loopback()
158
.port(8080)
159
.path("/")
160
.toURLUnchecked();
161
HttpURLConnection uc = (HttpURLConnection) url.openConnection(proxy);
162
uc.setRequestMethod("HEAD");
163
uc.getInputStream().readAllBytes();
164
}
165
}
166
167
static void testTunnel() throws Exception {
168
InetAddress loopback = InetAddress.getLoopbackAddress();
169
try (NTLMServer server = startServer(new ServerSocket(0, 0, loopback), Mode.TUNNEL)) {
170
SocketAddress proxyAddr = new InetSocketAddress(loopback, server.getLocalPort());
171
Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP, proxyAddr);
172
URL url = URIBuilder.newBuilder()
173
.scheme("https")
174
.loopback()
175
.port(8443)
176
.path("/")
177
.toURLUnchecked();
178
HttpURLConnection uc = (HttpURLConnection) url.openConnection(proxy);
179
uc.setRequestMethod("HEAD");
180
try {
181
uc.getInputStream().readAllBytes();
182
} catch (IOException e) {
183
// can be SocketException or SSLHandshakeException
184
// Tunnel established and closed by server
185
System.out.println("Tunnel established successfully");
186
} catch (NoSuchElementException e) {
187
System.err.println("Error: cannot read 200 response code");
188
throw e;
189
}
190
}
191
}
192
193
static class NTLMServer extends Thread implements AutoCloseable {
194
final ServerSocket ss;
195
final Mode mode;
196
volatile boolean closed;
197
198
NTLMServer(ServerSocket serverSS, Mode mode) {
199
super();
200
setDaemon(true);
201
this.ss = serverSS;
202
this.mode = mode;
203
}
204
205
int getLocalPort() { return ss.getLocalPort(); }
206
207
@Override
208
public void run() {
209
boolean doing2ndStageNTLM = false;
210
while (!closed) {
211
try {
212
Socket s = ss.accept();
213
InputStream is = s.getInputStream();
214
OutputStream os = s.getOutputStream();
215
switch(mode) {
216
case SERVER:
217
doServer(is, os, doing2ndStageNTLM);
218
break;
219
case PROXY:
220
doProxy(is, os, doing2ndStageNTLM);
221
break;
222
case TUNNEL:
223
doTunnel(is, os, doing2ndStageNTLM);
224
break;
225
default: throw new IllegalArgumentException();
226
}
227
if (!doing2ndStageNTLM) {
228
doing2ndStageNTLM = true;
229
} else {
230
os.close();
231
}
232
} catch (IOException ioe) {
233
if (!closed) {
234
ioe.printStackTrace();
235
}
236
}
237
}
238
}
239
240
@Override
241
public void close() {
242
if (closed) return;
243
synchronized(this) {
244
if (closed) return;
245
closed = true;
246
}
247
try { ss.close(); } catch (IOException x) { };
248
}
249
}
250
251
static NTLMServer startServer(ServerSocket serverSS, Mode mode) {
252
NTLMServer server = new NTLMServer(serverSS, mode);
253
server.start();
254
return server;
255
}
256
257
static String generateBody(int length) {
258
StringBuilder sb = new StringBuilder();
259
for(int i = 0; i < length; i++) {
260
sb.append(i % 10);
261
}
262
return sb.toString();
263
}
264
265
static void doServer(InputStream is, OutputStream os, boolean doing2ndStageNTLM) throws IOException {
266
if (!doing2ndStageNTLM) {
267
new MessageHeader(is);
268
os.write(RESP_SERVER_AUTH.getBytes("ASCII"));
269
} else {
270
new MessageHeader(is);
271
os.write(RESP_SERVER_NTLM.getBytes("ASCII"));
272
new MessageHeader(is);
273
os.write(RESP_SERVER_OR_PROXY_DEST.getBytes("ASCII"));
274
}
275
}
276
277
static void doProxy(InputStream is, OutputStream os, boolean doing2ndStageNTLM) throws IOException {
278
if (!doing2ndStageNTLM) {
279
new MessageHeader(is);
280
os.write(RESP_PROXY_AUTH.getBytes("ASCII"));
281
} else {
282
new MessageHeader(is);
283
os.write(RESP_PROXY_NTLM.getBytes("ASCII"));
284
new MessageHeader(is);
285
os.write(RESP_SERVER_OR_PROXY_DEST.getBytes("ASCII"));
286
}
287
}
288
289
static void doTunnel(InputStream is, OutputStream os, boolean doing2ndStageNTLM) throws IOException {
290
if (!doing2ndStageNTLM) {
291
new MessageHeader(is);
292
os.write(RESP_TUNNEL_AUTH.getBytes("ASCII"));
293
} else {
294
new MessageHeader(is);
295
os.write(RESP_TUNNEL_NTLM.getBytes("ASCII"));
296
new MessageHeader(is);
297
os.write(RESP_TUNNEL_ESTABLISHED.getBytes("ASCII"));
298
}
299
}
300
301
static class TestAuthenticator extends java.net.Authenticator {
302
protected PasswordAuthentication getPasswordAuthentication() {
303
return new PasswordAuthentication("test", "secret".toCharArray());
304
}
305
}
306
}
307
308