| /* |
| * Copyright (c) 2018, 2021 Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.security.ssl; |
| |
| import java.io.EOFException; |
| import java.io.IOException; |
| import java.io.InterruptedIOException; |
| import java.net.SocketException; |
| import java.nio.ByteBuffer; |
| import javax.crypto.AEADBadTagException; |
| import javax.crypto.BadPaddingException; |
| import javax.net.ssl.SSLHandshakeException; |
| |
| /** |
| * Interface for SSL/(D)TLS transportation. |
| */ |
| interface SSLTransport { |
| |
| /** |
| * Returns the host name of the peer. |
| * |
| * @return the host name of the peer, or null if nothing is |
| * available. |
| */ |
| String getPeerHost(); |
| |
| /** |
| * Returns the port number of the peer. |
| * |
| * @return the port number of the peer, or -1 if nothing is |
| * available. |
| */ |
| int getPeerPort(); |
| |
| /** |
| * Shutdown the transport. |
| */ |
| default void shutdown() throws IOException { |
| // blank |
| } |
| |
| /** |
| * Return true if delegated tasks used for handshaking operations. |
| * |
| * @return true if delegated tasks used for handshaking operations. |
| */ |
| boolean useDelegatedTask(); |
| |
| /** |
| * Decodes an array of SSL/(D)TLS network source data into the |
| * destination application data buffers. |
| * |
| * For SSL/TLS connections, if no source data, the network data may be |
| * received from the underlying underlying SSL/TLS input stream. |
| * |
| * @param context the transportation context |
| * @param srcs an array of {@code ByteBuffers} containing the |
| * inbound network data |
| * @param srcsOffset The offset within the {@code srcs} buffer array |
| * of the first buffer from which bytes are to be |
| * retrieved; it must be non-negative and no larger |
| * than {@code srcs.length}. |
| * @param srcsLength The maximum number of {@code srcs} buffers to be |
| * accessed; it must be non-negative and no larger than |
| * {@code srcs.length} - {@code srcsOffset}. |
| * @param dsts an array of {@code ByteBuffers} to hold inbound |
| * application data |
| * @param dstsOffset The offset within the {@code dsts} buffer array |
| * of the first buffer from which bytes are to be |
| * placed; it must be non-negative and no larger |
| * than {@code dsts.length}. |
| * @param dstsLength The maximum number of {@code dsts} buffers to be |
| * accessed; it must be non-negative and no larger than |
| * {@code dsts.length} - {@code dstsOffset}. |
| * |
| * @return a {@code Plaintext} describing the result of |
| * the operation |
| * @throws IOException if a problem was encountered while receiving or |
| * decoding networking data |
| */ |
| static Plaintext decode(TransportContext context, |
| ByteBuffer[] srcs, int srcsOffset, int srcsLength, |
| ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { |
| |
| Plaintext[] plaintexts = null; |
| try { |
| plaintexts = |
| context.inputRecord.decode(srcs, srcsOffset, srcsLength); |
| } catch (UnsupportedOperationException unsoe) { // SSLv2Hello |
| // Hack code to deliver SSLv2 error message for SSL/TLS connections. |
| if (!context.sslContext.isDTLS()) { |
| context.outputRecord.encodeV2NoCipher(); |
| if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
| SSLLogger.finest("may be talking to SSLv2"); |
| } |
| } |
| |
| throw context.fatal(Alert.UNEXPECTED_MESSAGE, unsoe); |
| } catch (AEADBadTagException bte) { |
| throw context.fatal(Alert.BAD_RECORD_MAC, bte); |
| } catch (BadPaddingException bpe) { |
| /* |
| * The basic SSLv3 record protection involves (optional) |
| * encryption for privacy, and an integrity check ensuring |
| * data origin authentication. We do them both here, and |
| * throw a fatal alert if the integrity check fails. |
| */ |
| Alert alert = (context.handshakeContext != null) ? |
| Alert.HANDSHAKE_FAILURE : |
| Alert.BAD_RECORD_MAC; |
| throw context.fatal(alert, bpe); |
| } catch (SSLHandshakeException she) { |
| // may be record sequence number overflow |
| throw context.fatal(Alert.HANDSHAKE_FAILURE, she); |
| } catch (EOFException eofe) { |
| // rethrow EOFException, the call will handle it if neede. |
| throw eofe; |
| } catch (InterruptedIOException | SocketException se) { |
| // don't close the Socket in case of timeouts or interrupts or SocketException. |
| throw se; |
| } catch (IOException ioe) { |
| throw context.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
| } |
| |
| if (plaintexts == null || plaintexts.length == 0) { |
| // Connection closed or record should be discarded. |
| return Plaintext.PLAINTEXT_NULL; |
| } |
| |
| Plaintext finalPlaintext = Plaintext.PLAINTEXT_NULL; |
| for (Plaintext plainText : plaintexts) { |
| // plainText should never be null for TLS protocols |
| if (plainText == Plaintext.PLAINTEXT_NULL) { |
| // Only happens for DTLS protocols. |
| // |
| // Received a retransmitted flight, and need to retransmit the |
| // previous delivered handshake flight messages. |
| if (context.handshakeContext != null && |
| context.handshakeContext.sslConfig.enableRetransmissions && |
| context.sslContext.isDTLS()) { |
| if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { |
| SSLLogger.finest("retransmited handshake flight"); |
| } |
| |
| context.outputRecord.launchRetransmission(); |
| } // Otherwise, discard the retransmitted flight. |
| } else if (plainText != null && |
| plainText.contentType != ContentType.APPLICATION_DATA.id) { |
| context.dispatch(plainText); |
| } |
| |
| if (plainText == null) { |
| plainText = Plaintext.PLAINTEXT_NULL; |
| } else if (plainText.contentType == |
| ContentType.APPLICATION_DATA.id) { |
| // check handshake status |
| // |
| // Note that JDK does not support 0-RTT yet. Otherwise, it is |
| // needed to check early_data. |
| if (!context.isNegotiated) { |
| if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { |
| SSLLogger.warning("unexpected application data " + |
| "before handshake completion"); |
| } |
| |
| throw context.fatal(Alert.UNEXPECTED_MESSAGE, |
| "Receiving application data before handshake complete"); |
| } |
| |
| // Fill the destination buffers. |
| if ((dsts != null) && (dstsLength > 0)) { |
| ByteBuffer fragment = plainText.fragment; |
| int remains = fragment.remaining(); |
| |
| // Should have enough room in the destination buffers. |
| int limit = dstsOffset + dstsLength; |
| for (int i = dstsOffset; |
| ((i < limit) && (remains > 0)); i++) { |
| |
| int amount = Math.min(dsts[i].remaining(), remains); |
| fragment.limit(fragment.position() + amount); |
| dsts[i].put(fragment); |
| remains -= amount; |
| |
| if (!dsts[i].hasRemaining()) { |
| dstsOffset++; |
| } |
| } |
| |
| if (remains > 0) { |
| throw context.fatal(Alert.INTERNAL_ERROR, |
| "no sufficient room in the destination buffers"); |
| } |
| } |
| } |
| |
| finalPlaintext = plainText; |
| } |
| |
| return finalPlaintext; |
| } |
| } |