src/java.net.http/share/classes/java/net/http/internal/SSLDelegate.java
branchhttp-client-branch
changeset 56089 42208b2f224e
parent 56079 d23b02f37fce
equal deleted inserted replaced
56088:38fac6d0521d 56089:42208b2f224e
       
     1 /*
       
     2  * Copyright (c) 2015, 2018, 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.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    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 java.net.http.internal;
       
    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 java.net.http.internal.common.Log;
       
    37 import java.net.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 
       
   112     int app_buf_size;
       
   113     int packet_buf_size;
       
   114 
       
   115     enum BufType {
       
   116         PACKET,
       
   117         APPLICATION
       
   118     }
       
   119 
       
   120     ByteBuffer allocate (BufType type) {
       
   121         return allocate (type, -1);
       
   122     }
       
   123 
       
   124     // TODO: Use buffer pool for this
       
   125     ByteBuffer allocate (BufType type, int len) {
       
   126         assert engine != null;
       
   127         synchronized (this) {
       
   128             int size;
       
   129             if (type == BufType.PACKET) {
       
   130                 if (packet_buf_size == 0) {
       
   131                     SSLSession sess = engine.getSession();
       
   132                     packet_buf_size = sess.getPacketBufferSize();
       
   133                 }
       
   134                 if (len > packet_buf_size) {
       
   135                     packet_buf_size = len;
       
   136                 }
       
   137                 size = packet_buf_size;
       
   138             } else {
       
   139                 if (app_buf_size == 0) {
       
   140                     SSLSession sess = engine.getSession();
       
   141                     app_buf_size = sess.getApplicationBufferSize();
       
   142                 }
       
   143                 if (len > app_buf_size) {
       
   144                     app_buf_size = len;
       
   145                 }
       
   146                 size = app_buf_size;
       
   147             }
       
   148             return ByteBuffer.allocate (size);
       
   149         }
       
   150     }
       
   151 
       
   152     /* reallocates the buffer by :-
       
   153      * 1. creating a new buffer double the size of the old one
       
   154      * 2. putting the contents of the old buffer into the new one
       
   155      * 3. set xx_buf_size to the new size if it was smaller than new size
       
   156      *
       
   157      * flip is set to true if the old buffer needs to be flipped
       
   158      * before it is copied.
       
   159      */
       
   160     private ByteBuffer realloc (ByteBuffer b, boolean flip, BufType type) {
       
   161         // TODO: there should be the linear growth, rather than exponential as
       
   162         // we definitely know the maximum amount of space required to unwrap
       
   163         synchronized (this) {
       
   164             int nsize = 2 * b.capacity();
       
   165             ByteBuffer n = allocate (type, nsize);
       
   166             if (flip) {
       
   167                 b.flip();
       
   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         {
       
   225             if (closed && !ignoreClose) {
       
   226                 throw new IOException ("Engine is closed");
       
   227             }
       
   228             Status status;
       
   229             WrapperResult r = new WrapperResult();
       
   230             synchronized (wrapLock) {
       
   231                 wrap_dst.clear();
       
   232                 do {
       
   233                     r.result = engine.wrap (src, offset, len, wrap_dst);
       
   234                     status = r.result.getStatus();
       
   235                     if (status == Status.BUFFER_OVERFLOW) {
       
   236                         wrap_dst = realloc (wrap_dst, true, BufType.PACKET);
       
   237                     }
       
   238                 } while (status == Status.BUFFER_OVERFLOW);
       
   239                 if (status == Status.CLOSED && !ignoreClose) {
       
   240                     closed = true;
       
   241                     return r;
       
   242                 }
       
   243                 if (r.result.bytesProduced() > 0) {
       
   244                     wrap_dst.flip();
       
   245                     int l = wrap_dst.remaining();
       
   246                     assert l == r.result.bytesProduced();
       
   247                     while (l>0) {
       
   248                         l -= chan.write (wrap_dst);
       
   249                     }
       
   250                 }
       
   251             }
       
   252             return r;
       
   253         }
       
   254 
       
   255         /* block until a complete message is available and return it
       
   256          * in dst, together with the Result. dst may have been re-allocated
       
   257          * so caller should check the returned value in Result
       
   258          * If handshaking is in progress then, possibly no data is returned
       
   259          */
       
   260         WrapperResult recvAndUnwrap(ByteBuffer dst) throws IOException {
       
   261             Status status;
       
   262             WrapperResult r = new WrapperResult();
       
   263             r.buf = dst;
       
   264             if (closed) {
       
   265                 throw new IOException ("Engine is closed");
       
   266             }
       
   267             boolean needData;
       
   268             if (u_remaining > 0) {
       
   269                 unwrap_src.compact();
       
   270                 unwrap_src.flip();
       
   271                 needData = false;
       
   272             } else {
       
   273                 unwrap_src.clear();
       
   274                 needData = true;
       
   275             }
       
   276             synchronized (unwrapLock) {
       
   277                 int x;
       
   278                 do {
       
   279                     if (needData) {
       
   280                         x = chan.read (unwrap_src);
       
   281                         if (x == -1) {
       
   282                             throw new IOException ("connection closed for reading");
       
   283                         }
       
   284                         unwrap_src.flip();
       
   285                     }
       
   286                     r.result = engine.unwrap (unwrap_src, r.buf);
       
   287                     status = r.result.getStatus();
       
   288                     if (status == Status.BUFFER_UNDERFLOW) {
       
   289                         if (unwrap_src.limit() == unwrap_src.capacity()) {
       
   290                             /* buffer not big enough */
       
   291                             unwrap_src = realloc (
       
   292                                 unwrap_src, false, BufType.PACKET
       
   293                             );
       
   294                         } else {
       
   295                             /* Buffer not full, just need to read more
       
   296                              * data off the channel. Reset pointers
       
   297                              * for reading off SocketChannel
       
   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             {
       
   343                 doHandshake(hs_status);
       
   344             }
       
   345         }
       
   346         return r;
       
   347     }
       
   348 
       
   349     /**
       
   350      * read data thru the engine into the given ByteBuffer. If the
       
   351      * given buffer was not large enough, a new one is allocated
       
   352      * and returned. This call handles handshaking automatically.
       
   353      * Caller should check if engine has been closed.
       
   354      */
       
   355     WrapperResult recvData (ByteBuffer dst) throws IOException {
       
   356         /* we wait until some user data arrives */
       
   357         int mark = dst.position();
       
   358         WrapperResult r = null;
       
   359         int pos = dst.position();
       
   360         while (dst.position() == pos) {
       
   361             r = wrapper.recvAndUnwrap (dst);
       
   362             dst = (r.buf != dst) ? r.buf: dst;
       
   363             Status status = r.result.getStatus();
       
   364             if (status == Status.CLOSED) {
       
   365                 doClosure ();
       
   366                 return r;
       
   367             }
       
   368 
       
   369             HandshakeStatus hs_status = r.result.getHandshakeStatus();
       
   370             if (hs_status != HandshakeStatus.FINISHED &&
       
   371                 hs_status != HandshakeStatus.NOT_HANDSHAKING)
       
   372             {
       
   373                 doHandshake (hs_status);
       
   374             }
       
   375         }
       
   376         Utils.flipToMark(dst, mark);
       
   377         return r;
       
   378     }
       
   379 
       
   380     /* we've received a close notify. Need to call wrap to send
       
   381      * the response
       
   382      */
       
   383     void doClosure () throws IOException {
       
   384         try {
       
   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:
       
   426                         tmp.clear();
       
   427                         tmp.flip();
       
   428                         r = wrapper.wrapAndSend(tmp, false);
       
   429                         break;
       
   430 
       
   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 }