11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.incubator.http;
27
28 import java.io.IOException;
29 import java.nio.ByteBuffer;
30 import java.nio.channels.SocketChannel;
31 import java.util.Arrays;
32 import java.util.List;
33 import java.util.concurrent.locks.Lock;
34 import java.util.concurrent.locks.ReentrantLock;
35 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
36 import javax.net.ssl.SSLEngineResult.Status;
37 import javax.net.ssl.*;
38 import jdk.incubator.http.internal.common.Log;
39 import jdk.incubator.http.internal.common.Utils;
40 import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
41
42 /**
43 * Implements the mechanics of SSL by managing an SSLEngine object.
44 * One of these is associated with each SSLConnection.
45 */
46 class SSLDelegate {
47
48 final SSLEngine engine;
49 final EngineWrapper wrapper;
50 final Lock handshaking = new ReentrantLock();
51 final SSLParameters sslParameters;
52 final SocketChannel chan;
53 final HttpClientImpl client;
54 final String serverName;
55
56 SSLDelegate(SSLEngine eng, SocketChannel chan, HttpClientImpl client, String sn)
57 {
58 this.engine = eng;
59 this.chan = chan;
60 this.client = client;
61 this.wrapper = new EngineWrapper(chan, engine);
62 this.sslParameters = engine.getSSLParameters();
63 this.serverName = sn;
64 }
65
66 // alpn[] may be null
67 SSLDelegate(SocketChannel chan, HttpClientImpl client, String[] alpn, String sn)
68 throws IOException
69 {
70 serverName = sn;
71 SSLContext context = client.sslContext();
72 engine = context.createSSLEngine();
73 engine.setUseClientMode(true);
74 SSLParameters sslp = client.sslParameters()
75 .orElseGet(context::getSupportedSSLParameters);
76 sslParameters = Utils.copySSLParameters(sslp);
77 if (sn != null) {
78 SNIHostName sni = new SNIHostName(sn);
79 sslParameters.setServerNames(List.of(sni));
80 }
81 if (alpn != null) {
82 sslParameters.setApplicationProtocols(alpn);
83 Log.logSSL("SSLDelegate: Setting application protocols: {0}" + Arrays.toString(alpn));
84 } else {
85 Log.logSSL("SSLDelegate: No application protocols proposed");
86 }
87 engine.setSSLParameters(sslParameters);
88 wrapper = new EngineWrapper(chan, engine);
89 this.chan = chan;
90 this.client = client;
91 }
92
93 SSLParameters getSSLParameters() {
94 return sslParameters;
95 }
96
97 private static long countBytes(ByteBuffer[] buffers, int start, int number) {
98 long c = 0;
99 for (int i=0; i<number; i++) {
100 c+= buffers[start+i].remaining();
101 }
102 return c;
103 }
104
105
106 static class WrapperResult {
107 static WrapperResult createOK() {
108 WrapperResult r = new WrapperResult();
109 r.buf = null;
110 r.result = new SSLEngineResult(Status.OK, NOT_HANDSHAKING, 0, 0);
111 return r;
112 }
113 SSLEngineResult result;
114
115 ByteBuffer buf; // buffer containing result data
116 }
117
174 }
175 n.put(b);
176 b = n;
177 }
178 return b;
179 }
180
181 /**
182 * This is a thin wrapper over SSLEngine and the SocketChannel, which
183 * guarantees the ordering of wraps/unwraps with respect to the underlying
184 * channel read/writes. It handles the UNDER/OVERFLOW status codes
185 * It does not handle the handshaking status codes, or the CLOSED status code
186 * though once the engine is closed, any attempt to read/write to it
187 * will get an exception. The overall result is returned.
188 * It functions synchronously/blocking
189 */
190 class EngineWrapper {
191
192 SocketChannel chan;
193 SSLEngine engine;
194 Object wrapLock, unwrapLock;
195 ByteBuffer unwrap_src, wrap_dst;
196 boolean closed = false;
197 int u_remaining; // the number of bytes left in unwrap_src after an unwrap()
198
199 EngineWrapper (SocketChannel chan, SSLEngine engine) {
200 this.chan = chan;
201 this.engine = engine;
202 wrapLock = new Object();
203 unwrapLock = new Object();
204 unwrap_src = allocate(BufType.PACKET);
205 wrap_dst = allocate(BufType.PACKET);
206 }
207
208 void close () throws IOException {
209 }
210
211 WrapperResult wrapAndSend(ByteBuffer src, boolean ignoreClose)
212 throws IOException
213 {
214 ByteBuffer[] buffers = new ByteBuffer[1];
215 buffers[0] = src;
216 return wrapAndSend(buffers, 0, 1, ignoreClose);
217 }
218
219 /* try to wrap and send the data in src. Handles OVERFLOW.
220 * Might block if there is an outbound blockage or if another
221 * thread is calling wrap(). Also, might not send any data
222 * if an unwrap is needed.
223 */
224 WrapperResult wrapAndSend(ByteBuffer[] src,
225 int offset,
226 int len,
227 boolean ignoreClose)
228 throws IOException
229 {
303 */
304 unwrap_src.position (unwrap_src.limit());
305 unwrap_src.limit (unwrap_src.capacity());
306 }
307 needData = true;
308 } else if (status == Status.BUFFER_OVERFLOW) {
309 r.buf = realloc (r.buf, true, BufType.APPLICATION);
310 needData = false;
311 } else if (status == Status.CLOSED) {
312 closed = true;
313 r.buf.flip();
314 return r;
315 }
316 } while (status != Status.OK);
317 }
318 u_remaining = unwrap_src.remaining();
319 return r;
320 }
321 }
322
323 WrapperResult sendData (ByteBuffer src) throws IOException {
324 ByteBuffer[] buffers = new ByteBuffer[1];
325 buffers[0] = src;
326 return sendData(buffers, 0, 1);
327 }
328
329 /**
330 * send the data in the given ByteBuffer. If a handshake is needed
331 * then this is handled within this method. When this call returns,
332 * all of the given user data has been sent and any handshake has been
333 * completed. Caller should check if engine has been closed.
334 */
335 WrapperResult sendData (ByteBuffer[] src, int offset, int len) throws IOException {
336 WrapperResult r = WrapperResult.createOK();
337 while (countBytes(src, offset, len) > 0) {
338 r = wrapper.wrapAndSend(src, offset, len, false);
339 Status status = r.result.getStatus();
340 if (status == Status.CLOSED) {
341 doClosure ();
342 return r;
343 }
344 HandshakeStatus hs_status = r.result.getHandshakeStatus();
345 if (hs_status != HandshakeStatus.FINISHED &&
346 hs_status != HandshakeStatus.NOT_HANDSHAKING)
347 {
390 handshaking.lock();
391 ByteBuffer tmp = allocate(BufType.APPLICATION);
392 WrapperResult r;
393 do {
394 tmp.clear();
395 tmp.flip ();
396 r = wrapper.wrapAndSend(tmp, true);
397 } while (r.result.getStatus() != Status.CLOSED);
398 } finally {
399 handshaking.unlock();
400 }
401 }
402
403 /* do the (complete) handshake after acquiring the handshake lock.
404 * If two threads call this at the same time, then we depend
405 * on the wrapper methods being idempotent. eg. if wrapAndSend()
406 * is called with no data to send then there must be no problem
407 */
408 @SuppressWarnings("fallthrough")
409 void doHandshake (HandshakeStatus hs_status) throws IOException {
410 boolean wasBlocking = false;
411 try {
412 wasBlocking = chan.isBlocking();
413 handshaking.lock();
414 chan.configureBlocking(true);
415 ByteBuffer tmp = allocate(BufType.APPLICATION);
416 while (hs_status != HandshakeStatus.FINISHED &&
417 hs_status != HandshakeStatus.NOT_HANDSHAKING)
418 {
419 WrapperResult r = null;
420 switch (hs_status) {
421 case NEED_TASK:
422 Runnable task;
423 while ((task = engine.getDelegatedTask()) != null) {
424 /* run in current thread, because we are already
425 * running an external Executor
426 */
427 task.run();
428 }
429 /* fall thru - call wrap again */
430 case NEED_WRAP:
436 case NEED_UNWRAP:
437 tmp.clear();
438 r = wrapper.recvAndUnwrap (tmp);
439 if (r.buf != tmp) {
440 tmp = r.buf;
441 }
442 assert tmp.position() == 0;
443 break;
444 }
445 hs_status = r.result.getHandshakeStatus();
446 }
447 Log.logSSL(getSessionInfo());
448 if (!wasBlocking) {
449 chan.configureBlocking(false);
450 }
451 } finally {
452 handshaking.unlock();
453 }
454 }
455
456 static void printParams(SSLParameters p) {
457 System.out.println("SSLParameters:");
458 if (p == null) {
459 System.out.println("Null params");
460 return;
461 }
462 for (String cipher : p.getCipherSuites()) {
463 System.out.printf("cipher: %s\n", cipher);
464 }
465 // JDK 8 EXCL START
466 for (String approto : p.getApplicationProtocols()) {
467 System.out.printf("application protocol: %s\n", approto);
468 }
469 // JDK 8 EXCL END
470 for (String protocol : p.getProtocols()) {
471 System.out.printf("protocol: %s\n", protocol);
472 }
473 if (p.getServerNames() != null) {
474 for (SNIServerName sname : p.getServerNames()) {
475 System.out.printf("server name: %s\n", sname.toString());
476 }
477 }
478 }
479
480 String getSessionInfo() {
481 StringBuilder sb = new StringBuilder();
482 String application = engine.getApplicationProtocol();
483 SSLSession sess = engine.getSession();
484 String cipher = sess.getCipherSuite();
485 String protocol = sess.getProtocol();
486 sb.append("Handshake complete alpn: ")
487 .append(application)
488 .append(", Cipher: ")
489 .append(cipher)
490 .append(", Protocol: ")
491 .append(protocol);
492 return sb.toString();
493 }
494 }
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.incubator.http;
27
28 import java.io.IOException;
29 import java.nio.ByteBuffer;
30 import java.nio.channels.SocketChannel;
31 import java.util.concurrent.locks.Lock;
32 import java.util.concurrent.locks.ReentrantLock;
33 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
34 import javax.net.ssl.SSLEngineResult.Status;
35 import javax.net.ssl.*;
36 import jdk.incubator.http.internal.common.Log;
37 import jdk.incubator.http.internal.common.Utils;
38 import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
39
40 /**
41 * Implements the mechanics of SSL by managing an SSLEngine object.
42 * <p>
43 * This class is only used to implement the {@link
44 * AbstractAsyncSSLConnection.SSLConnectionChannel} which is handed of
45 * to RawChannelImpl when creating a WebSocket.
46 */
47 class SSLDelegate {
48
49 final SSLEngine engine;
50 final EngineWrapper wrapper;
51 final Lock handshaking = new ReentrantLock();
52 final SocketChannel chan;
53
54 SSLDelegate(SSLEngine eng, SocketChannel chan)
55 {
56 this.engine = eng;
57 this.chan = chan;
58 this.wrapper = new EngineWrapper(chan, engine);
59 }
60
61 // alpn[] may be null
62 // SSLDelegate(SocketChannel chan, HttpClientImpl client, String[] alpn, String sn)
63 // throws IOException
64 // {
65 // serverName = sn;
66 // SSLContext context = client.sslContext();
67 // engine = context.createSSLEngine();
68 // engine.setUseClientMode(true);
69 // SSLParameters sslp = client.sslParameters();
70 // sslParameters = Utils.copySSLParameters(sslp);
71 // if (sn != null) {
72 // SNIHostName sni = new SNIHostName(sn);
73 // sslParameters.setServerNames(List.of(sni));
74 // }
75 // if (alpn != null) {
76 // sslParameters.setApplicationProtocols(alpn);
77 // Log.logSSL("SSLDelegate: Setting application protocols: {0}" + Arrays.toString(alpn));
78 // } else {
79 // Log.logSSL("SSLDelegate: No application protocols proposed");
80 // }
81 // engine.setSSLParameters(sslParameters);
82 // wrapper = new EngineWrapper(chan, engine);
83 // this.chan = chan;
84 // this.client = client;
85 // }
86
87 // SSLParameters getSSLParameters() {
88 // return sslParameters;
89 // }
90
91 static long countBytes(ByteBuffer[] buffers, int start, int number) {
92 long c = 0;
93 for (int i=0; i<number; i++) {
94 c+= buffers[start+i].remaining();
95 }
96 return c;
97 }
98
99
100 static class WrapperResult {
101 static WrapperResult createOK() {
102 WrapperResult r = new WrapperResult();
103 r.buf = null;
104 r.result = new SSLEngineResult(Status.OK, NOT_HANDSHAKING, 0, 0);
105 return r;
106 }
107 SSLEngineResult result;
108
109 ByteBuffer buf; // buffer containing result data
110 }
111
168 }
169 n.put(b);
170 b = n;
171 }
172 return b;
173 }
174
175 /**
176 * This is a thin wrapper over SSLEngine and the SocketChannel, which
177 * guarantees the ordering of wraps/unwraps with respect to the underlying
178 * channel read/writes. It handles the UNDER/OVERFLOW status codes
179 * It does not handle the handshaking status codes, or the CLOSED status code
180 * though once the engine is closed, any attempt to read/write to it
181 * will get an exception. The overall result is returned.
182 * It functions synchronously/blocking
183 */
184 class EngineWrapper {
185
186 SocketChannel chan;
187 SSLEngine engine;
188 final Object wrapLock;
189 final Object unwrapLock;
190 ByteBuffer unwrap_src, wrap_dst;
191 boolean closed = false;
192 int u_remaining; // the number of bytes left in unwrap_src after an unwrap()
193
194 EngineWrapper (SocketChannel chan, SSLEngine engine) {
195 this.chan = chan;
196 this.engine = engine;
197 wrapLock = new Object();
198 unwrapLock = new Object();
199 unwrap_src = allocate(BufType.PACKET);
200 wrap_dst = allocate(BufType.PACKET);
201 }
202
203 // void close () throws IOException {
204 // }
205
206 WrapperResult wrapAndSend(ByteBuffer src, boolean ignoreClose)
207 throws IOException
208 {
209 ByteBuffer[] buffers = new ByteBuffer[1];
210 buffers[0] = src;
211 return wrapAndSend(buffers, 0, 1, ignoreClose);
212 }
213
214 /* try to wrap and send the data in src. Handles OVERFLOW.
215 * Might block if there is an outbound blockage or if another
216 * thread is calling wrap(). Also, might not send any data
217 * if an unwrap is needed.
218 */
219 WrapperResult wrapAndSend(ByteBuffer[] src,
220 int offset,
221 int len,
222 boolean ignoreClose)
223 throws IOException
224 {
298 */
299 unwrap_src.position (unwrap_src.limit());
300 unwrap_src.limit (unwrap_src.capacity());
301 }
302 needData = true;
303 } else if (status == Status.BUFFER_OVERFLOW) {
304 r.buf = realloc (r.buf, true, BufType.APPLICATION);
305 needData = false;
306 } else if (status == Status.CLOSED) {
307 closed = true;
308 r.buf.flip();
309 return r;
310 }
311 } while (status != Status.OK);
312 }
313 u_remaining = unwrap_src.remaining();
314 return r;
315 }
316 }
317
318 // WrapperResult sendData (ByteBuffer src) throws IOException {
319 // ByteBuffer[] buffers = new ByteBuffer[1];
320 // buffers[0] = src;
321 // return sendData(buffers, 0, 1);
322 // }
323
324 /**
325 * send the data in the given ByteBuffer. If a handshake is needed
326 * then this is handled within this method. When this call returns,
327 * all of the given user data has been sent and any handshake has been
328 * completed. Caller should check if engine has been closed.
329 */
330 WrapperResult sendData (ByteBuffer[] src, int offset, int len) throws IOException {
331 WrapperResult r = WrapperResult.createOK();
332 while (countBytes(src, offset, len) > 0) {
333 r = wrapper.wrapAndSend(src, offset, len, false);
334 Status status = r.result.getStatus();
335 if (status == Status.CLOSED) {
336 doClosure ();
337 return r;
338 }
339 HandshakeStatus hs_status = r.result.getHandshakeStatus();
340 if (hs_status != HandshakeStatus.FINISHED &&
341 hs_status != HandshakeStatus.NOT_HANDSHAKING)
342 {
385 handshaking.lock();
386 ByteBuffer tmp = allocate(BufType.APPLICATION);
387 WrapperResult r;
388 do {
389 tmp.clear();
390 tmp.flip ();
391 r = wrapper.wrapAndSend(tmp, true);
392 } while (r.result.getStatus() != Status.CLOSED);
393 } finally {
394 handshaking.unlock();
395 }
396 }
397
398 /* do the (complete) handshake after acquiring the handshake lock.
399 * If two threads call this at the same time, then we depend
400 * on the wrapper methods being idempotent. eg. if wrapAndSend()
401 * is called with no data to send then there must be no problem
402 */
403 @SuppressWarnings("fallthrough")
404 void doHandshake (HandshakeStatus hs_status) throws IOException {
405 boolean wasBlocking;
406 try {
407 wasBlocking = chan.isBlocking();
408 handshaking.lock();
409 chan.configureBlocking(true);
410 ByteBuffer tmp = allocate(BufType.APPLICATION);
411 while (hs_status != HandshakeStatus.FINISHED &&
412 hs_status != HandshakeStatus.NOT_HANDSHAKING)
413 {
414 WrapperResult r = null;
415 switch (hs_status) {
416 case NEED_TASK:
417 Runnable task;
418 while ((task = engine.getDelegatedTask()) != null) {
419 /* run in current thread, because we are already
420 * running an external Executor
421 */
422 task.run();
423 }
424 /* fall thru - call wrap again */
425 case NEED_WRAP:
431 case NEED_UNWRAP:
432 tmp.clear();
433 r = wrapper.recvAndUnwrap (tmp);
434 if (r.buf != tmp) {
435 tmp = r.buf;
436 }
437 assert tmp.position() == 0;
438 break;
439 }
440 hs_status = r.result.getHandshakeStatus();
441 }
442 Log.logSSL(getSessionInfo());
443 if (!wasBlocking) {
444 chan.configureBlocking(false);
445 }
446 } finally {
447 handshaking.unlock();
448 }
449 }
450
451 // static void printParams(SSLParameters p) {
452 // System.out.println("SSLParameters:");
453 // if (p == null) {
454 // System.out.println("Null params");
455 // return;
456 // }
457 // for (String cipher : p.getCipherSuites()) {
458 // System.out.printf("cipher: %s\n", cipher);
459 // }
460 // // JDK 8 EXCL START
461 // for (String approto : p.getApplicationProtocols()) {
462 // System.out.printf("application protocol: %s\n", approto);
463 // }
464 // // JDK 8 EXCL END
465 // for (String protocol : p.getProtocols()) {
466 // System.out.printf("protocol: %s\n", protocol);
467 // }
468 // if (p.getServerNames() != null) {
469 // for (SNIServerName sname : p.getServerNames()) {
470 // System.out.printf("server name: %s\n", sname.toString());
471 // }
472 // }
473 // }
474
475 String getSessionInfo() {
476 StringBuilder sb = new StringBuilder();
477 String application = engine.getApplicationProtocol();
478 SSLSession sess = engine.getSession();
479 String cipher = sess.getCipherSuite();
480 String protocol = sess.getProtocol();
481 sb.append("Handshake complete alpn: ")
482 .append(application)
483 .append(", Cipher: ")
484 .append(cipher)
485 .append(", Protocol: ")
486 .append(protocol);
487 return sb.toString();
488 }
489 }
|