/* * Copyright (c) 2018, 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.nio.ByteBuffer; 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 (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 (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; } }