< prev index next >

src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLDelegate.java

Print this page




  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 }
< prev index next >