package sun.security.ssl;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLProtocolException;
import static sun.security.ssl.SSLExtension.CH_MAX_FRAGMENT_LENGTH;
import static sun.security.ssl.SSLExtension.EE_MAX_FRAGMENT_LENGTH;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import static sun.security.ssl.SSLExtension.SH_MAX_FRAGMENT_LENGTH;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
final class MaxFragExtension {
static final HandshakeProducer chNetworkProducer =
new CHMaxFragmentLengthProducer();
static final ExtensionConsumer chOnLoadConsumer =
new CHMaxFragmentLengthConsumer();
static final HandshakeProducer shNetworkProducer =
new SHMaxFragmentLengthProducer();
static final ExtensionConsumer shOnLoadConsumer =
new SHMaxFragmentLengthConsumer();
static final HandshakeConsumer shOnTradeConsumer =
new SHMaxFragmentLengthUpdate();
static final HandshakeProducer eeNetworkProducer =
new EEMaxFragmentLengthProducer();
static final ExtensionConsumer eeOnLoadConsumer =
new EEMaxFragmentLengthConsumer();
static final HandshakeConsumer eeOnTradeConsumer =
new EEMaxFragmentLengthUpdate();
static final SSLStringizer maxFragLenStringizer =
new MaxFragLenStringizer();
static final class MaxFragLenSpec implements SSLExtensionSpec {
byte id;
private MaxFragLenSpec(byte id) {
this.id = id;
}
private MaxFragLenSpec(ByteBuffer buffer) throws IOException {
if (buffer.remaining() != 1) {
throw new SSLProtocolException(
"Invalid max_fragment_length extension data");
}
this.id = buffer.get();
}
@Override
public String toString() {
return MaxFragLenEnum.nameOf(id);
}
}
private static final class MaxFragLenStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new MaxFragLenSpec(buffer)).toString();
} catch (IOException ioe) {
return ioe.getMessage();
}
}
}
static enum MaxFragLenEnum {
MFL_512 ((byte)0x01, 512, "2^9"),
MFL_1024 ((byte)0x02, 1024, "2^10"),
MFL_2048 ((byte)0x03, 2048, "2^11"),
MFL_4096 ((byte)0x04, 4096, "2^12");
final byte id;
final int fragmentSize;
final String description;
private MaxFragLenEnum(byte id, int fragmentSize, String description) {
this.id = id;
this.fragmentSize = fragmentSize;
this.description = description;
}
private static MaxFragLenEnum valueOf(byte id) {
for (MaxFragLenEnum mfl : MaxFragLenEnum.values()) {
if (mfl.id == id) {
return mfl;
}
}
return null;
}
private static String nameOf(byte id) {
for (MaxFragLenEnum mfl : MaxFragLenEnum.values()) {
if (mfl.id == id) {
return mfl.description;
}
}
return "UNDEFINED-MAX-FRAGMENT-LENGTH(" + id + ")";
}
static MaxFragLenEnum valueOf(int fragmentSize) {
if (fragmentSize <= 0) {
return null;
} else if (fragmentSize < 1024) {
return MFL_512;
} else if (fragmentSize < 2048) {
return MFL_1024;
} else if (fragmentSize < 4096) {
return MFL_2048;
} else if (fragmentSize == 4096) {
return MFL_4096;
}
return null;
}
}
private static final
class CHMaxFragmentLengthProducer implements HandshakeProducer {
private CHMaxFragmentLengthProducer() {
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
ClientHandshakeContext chc = (ClientHandshakeContext)context;
if (!chc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable max_fragment_length extension");
}
return null;
}
int requestedMFLength;
if (chc.isResumption && (chc.resumingSession != null)) {
requestedMFLength =
chc.resumingSession.getNegotiatedMaxFragSize();
} else if (chc.sslConfig.maximumPacketSize != 0) {
requestedMFLength = chc.sslConfig.maximumPacketSize -
SSLRecord.maxPlaintextPlusSize;
} else {
requestedMFLength = -1;
}
MaxFragLenEnum mfl = MaxFragLenEnum.valueOf(requestedMFLength);
if (mfl != null) {
chc.handshakeExtensions.put(
CH_MAX_FRAGMENT_LENGTH, new MaxFragLenSpec(mfl.id));
return new byte[] { mfl.id };
} else {
chc.maxFragmentLength = -1;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"No available max_fragment_length extension can " +
"be used for fragment size of " +
requestedMFLength + "bytes");
}
}
return null;
}
}
private static final
class CHMaxFragmentLengthConsumer implements ExtensionConsumer {
private CHMaxFragmentLengthConsumer() {
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
ServerHandshakeContext shc = (ServerHandshakeContext)context;
if (!shc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable max_fragment_length extension");
}
return;
}
MaxFragLenSpec spec;
try {
spec = new MaxFragLenSpec(buffer);
} catch (IOException ioe) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
if (mfle == null) {
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"the requested maximum fragment length is other " +
"than the allowed values");
}
shc.maxFragmentLength = mfle.fragmentSize;
shc.handshakeExtensions.put(CH_MAX_FRAGMENT_LENGTH, spec);
}
}
private static final
class SHMaxFragmentLengthProducer implements HandshakeProducer {
private SHMaxFragmentLengthProducer() {
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
ServerHandshakeContext shc = (ServerHandshakeContext)context;
MaxFragLenSpec spec = (MaxFragLenSpec)
shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
if (spec == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.finest(
"Ignore unavailable max_fragment_length extension");
}
return null;
}
if ((shc.maxFragmentLength > 0) &&
(shc.sslConfig.maximumPacketSize != 0)) {
int estimatedMaxFragSize =
shc.negotiatedCipherSuite.calculatePacketSize(
shc.maxFragmentLength, shc.negotiatedProtocol);
if (estimatedMaxFragSize > shc.sslConfig.maximumPacketSize) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Abort the maximum fragment length negotiation, " +
"may overflow the maximum packet size limit.");
}
shc.maxFragmentLength = -1;
}
}
if (shc.maxFragmentLength > 0) {
shc.handshakeSession.setNegotiatedMaxFragSize(
shc.maxFragmentLength);
shc.conContext.inputRecord.changeFragmentSize(
shc.maxFragmentLength);
shc.conContext.outputRecord.changeFragmentSize(
shc.maxFragmentLength);
shc.handshakeExtensions.put(SH_MAX_FRAGMENT_LENGTH, spec);
return new byte[] { spec.id };
}
return null;
}
}
private static final
class SHMaxFragmentLengthConsumer implements ExtensionConsumer {
private SHMaxFragmentLengthConsumer() {
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
ClientHandshakeContext chc = (ClientHandshakeContext)context;
MaxFragLenSpec requestedSpec = (MaxFragLenSpec)
chc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
if (requestedSpec == null) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected max_fragment_length extension in ServerHello");
}
MaxFragLenSpec spec;
try {
spec = new MaxFragLenSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
if (spec.id != requestedSpec.id) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"The maximum fragment length response is not requested");
}
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
if (mfle == null) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"the requested maximum fragment length is other " +
"than the allowed values");
}
chc.maxFragmentLength = mfle.fragmentSize;
chc.handshakeExtensions.put(SH_MAX_FRAGMENT_LENGTH, spec);
}
}
private static final class SHMaxFragmentLengthUpdate
implements HandshakeConsumer {
private SHMaxFragmentLengthUpdate() {
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message) throws IOException {
ClientHandshakeContext chc = (ClientHandshakeContext)context;
MaxFragLenSpec spec = (MaxFragLenSpec)
chc.handshakeExtensions.get(SH_MAX_FRAGMENT_LENGTH);
if (spec == null) {
return;
}
if ((chc.maxFragmentLength > 0) &&
(chc.sslConfig.maximumPacketSize != 0)) {
int estimatedMaxFragSize =
chc.negotiatedCipherSuite.calculatePacketSize(
chc.maxFragmentLength, chc.negotiatedProtocol);
if (estimatedMaxFragSize > chc.sslConfig.maximumPacketSize) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Abort the maximum fragment length negotiation, " +
"may overflow the maximum packet size limit.");
}
chc.maxFragmentLength = -1;
}
}
if (chc.maxFragmentLength > 0) {
chc.handshakeSession.setNegotiatedMaxFragSize(
chc.maxFragmentLength);
chc.conContext.inputRecord.changeFragmentSize(
chc.maxFragmentLength);
chc.conContext.outputRecord.changeFragmentSize(
chc.maxFragmentLength);
}
}
}
private static final
class EEMaxFragmentLengthProducer implements HandshakeProducer {
private EEMaxFragmentLengthProducer() {
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
ServerHandshakeContext shc = (ServerHandshakeContext)context;
MaxFragLenSpec spec = (MaxFragLenSpec)
shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
if (spec == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.finest(
"Ignore unavailable max_fragment_length extension");
}
return null;
}
if ((shc.maxFragmentLength > 0) &&
(shc.sslConfig.maximumPacketSize != 0)) {
int estimatedMaxFragSize =
shc.negotiatedCipherSuite.calculatePacketSize(
shc.maxFragmentLength, shc.negotiatedProtocol);
if (estimatedMaxFragSize > shc.sslConfig.maximumPacketSize) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Abort the maximum fragment length negotiation, " +
"may overflow the maximum packet size limit.");
}
shc.maxFragmentLength = -1;
}
}
if (shc.maxFragmentLength > 0) {
shc.handshakeSession.setNegotiatedMaxFragSize(
shc.maxFragmentLength);
shc.conContext.inputRecord.changeFragmentSize(
shc.maxFragmentLength);
shc.conContext.outputRecord.changeFragmentSize(
shc.maxFragmentLength);
shc.handshakeExtensions.put(EE_MAX_FRAGMENT_LENGTH, spec);
return new byte[] { spec.id };
}
return null;
}
}
private static final
class EEMaxFragmentLengthConsumer implements ExtensionConsumer {
private EEMaxFragmentLengthConsumer() {
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
ClientHandshakeContext chc = (ClientHandshakeContext)context;
MaxFragLenSpec requestedSpec = (MaxFragLenSpec)
chc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
if (requestedSpec == null) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected max_fragment_length extension in ServerHello");
}
MaxFragLenSpec spec;
try {
spec = new MaxFragLenSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
if (spec.id != requestedSpec.id) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"The maximum fragment length response is not requested");
}
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
if (mfle == null) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"the requested maximum fragment length is other " +
"than the allowed values");
}
chc.maxFragmentLength = mfle.fragmentSize;
chc.handshakeExtensions.put(EE_MAX_FRAGMENT_LENGTH, spec);
}
}
private static final
class EEMaxFragmentLengthUpdate implements HandshakeConsumer {
private EEMaxFragmentLengthUpdate() {
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message) throws IOException {
ClientHandshakeContext chc = (ClientHandshakeContext)context;
MaxFragLenSpec spec = (MaxFragLenSpec)
chc.handshakeExtensions.get(EE_MAX_FRAGMENT_LENGTH);
if (spec == null) {
return;
}
if ((chc.maxFragmentLength > 0) &&
(chc.sslConfig.maximumPacketSize != 0)) {
int estimatedMaxFragSize =
chc.negotiatedCipherSuite.calculatePacketSize(
chc.maxFragmentLength, chc.negotiatedProtocol);
if (estimatedMaxFragSize > chc.sslConfig.maximumPacketSize) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Abort the maximum fragment length negotiation, " +
"may overflow the maximum packet size limit.");
}
chc.maxFragmentLength = -1;
}
}
if (chc.maxFragmentLength > 0) {
chc.handshakeSession.setNegotiatedMaxFragSize(
chc.maxFragmentLength);
chc.conContext.inputRecord.changeFragmentSize(
chc.maxFragmentLength);
chc.conContext.outputRecord.changeFragmentSize(
chc.maxFragmentLength);
}
}
}
}