< prev index next >

src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java

Print this page

        

*** 1,7 **** /* ! * Copyright (c) 2006, 2012, 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 --- 1,7 ---- /* ! * Copyright (c) 2015, 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
*** 24,283 **** */ package sun.security.ssl; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; - import java.util.List; import java.util.LinkedHashMap; import java.util.Map; ! import javax.net.ssl.SNIHostName; import javax.net.ssl.SNIMatcher; import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLProtocolException; import javax.net.ssl.StandardConstants; ! /* ! * [RFC 4366/6066] To facilitate secure connections to servers that host ! * multiple 'virtual' servers at a single underlying network address, clients ! * MAY include an extension of type "server_name" in the (extended) client ! * hello. The "extension_data" field of this extension SHALL contain ! * "ServerNameList" where: ! * ! * struct { ! * NameType name_type; ! * select (name_type) { ! * case host_name: HostName; ! * } name; ! * } ServerName; ! * ! * enum { ! * host_name(0), (255) ! * } NameType; ! * ! * opaque HostName<1..2^16-1>; ! * ! * struct { ! * ServerName server_name_list<1..2^16-1> ! * } ServerNameList; */ ! final class ServerNameExtension extends HelloExtension { ! // For backward compatibility, all future data structures associated with ! // new NameTypes MUST begin with a 16-bit length field. ! static final int NAME_HEADER_LENGTH = 3; // NameType: 1 byte ! // Name length: 2 bytes ! private Map<Integer, SNIServerName> sniMap; ! private int listLength; // ServerNameList length ! ! // constructor for ServerHello ! ServerNameExtension() throws IOException { ! super(ExtensionType.EXT_SERVER_NAME); ! ! listLength = 0; ! sniMap = Collections.<Integer, SNIServerName>emptyMap(); ! } ! ! // constructor for ClientHello ! ServerNameExtension(List<SNIServerName> serverNames) ! throws IOException { ! super(ExtensionType.EXT_SERVER_NAME); ! ! listLength = 0; ! sniMap = new LinkedHashMap<>(); ! for (SNIServerName serverName : serverNames) { ! // check for duplicated server name type ! if (sniMap.put(serverName.getType(), serverName) != null) { ! // unlikely to happen, but in case ... ! throw new RuntimeException( ! "Duplicated server name of type " + serverName.getType()); ! } ! ! listLength += serverName.getEncoded().length + NAME_HEADER_LENGTH; } ! // This constructor is used for ClientHello only. Empty list is ! // not allowed in client mode. ! if (listLength == 0) { ! throw new RuntimeException("The ServerNameList cannot be empty"); ! } } ! // constructor for ServerHello for parsing SNI extension ! ServerNameExtension(HandshakeInStream s, int len) ! throws IOException { ! super(ExtensionType.EXT_SERVER_NAME); ! ! int remains = len; ! if (len >= 2) { // "server_name" extension in ClientHello ! listLength = s.getInt16(); // ServerNameList length ! if (listLength == 0 || listLength + 2 != len) { throw new SSLProtocolException( ! "Invalid " + type + " extension"); } ! remains -= 2; ! sniMap = new LinkedHashMap<>(); ! while (remains > 0) { ! int code = s.getInt8(); // NameType // HostName (length read in getBytes16); ! byte[] encoded = s.getBytes16(); ! SNIServerName serverName; ! switch (code) { ! case StandardConstants.SNI_HOST_NAME: if (encoded.length == 0) { throw new SSLProtocolException( ! "Empty HostName in server name indication"); } try { serverName = new SNIHostName(encoded); } catch (IllegalArgumentException iae) { SSLProtocolException spe = new SSLProtocolException( "Illegal server name, type=host_name(" + ! code + "), name=" + (new String(encoded, StandardCharsets.UTF_8)) + ! ", value=" + Debug.toString(encoded)); ! spe.initCause(iae); ! throw spe; } ! break; ! default: try { ! serverName = new UnknownServerName(code, encoded); } catch (IllegalArgumentException iae) { SSLProtocolException spe = new SSLProtocolException( ! "Illegal server name, type=(" + code + ! "), value=" + Debug.toString(encoded)); ! spe.initCause(iae); ! throw spe; } } // check for duplicated server name type if (sniMap.put(serverName.getType(), serverName) != null) { throw new SSLProtocolException( "Duplicated server name of type " + serverName.getType()); } ! remains -= encoded.length + NAME_HEADER_LENGTH; } ! } else if (len == 0) { // "server_name" extension in ServerHello ! listLength = 0; ! sniMap = Collections.<Integer, SNIServerName>emptyMap(); } ! if (remains != 0) { ! throw new SSLProtocolException("Invalid server_name extension"); } } ! List<SNIServerName> getServerNames() { ! if (sniMap != null && !sniMap.isEmpty()) { ! return Collections.<SNIServerName>unmodifiableList( ! new ArrayList<>(sniMap.values())); } ! return Collections.<SNIServerName>emptyList(); } ! /* ! * Is the extension recognized by the corresponding matcher? ! * ! * This method is used to check whether the server name indication can ! * be recognized by the server name matchers. ! * ! * Per RFC 6066, if the server understood the ClientHello extension but ! * does not recognize the server name, the server SHOULD take one of two ! * actions: either abort the handshake by sending a fatal-level ! * unrecognized_name(112) alert or continue the handshake. ! * ! * If there is an instance of SNIMatcher defined for a particular name ! * type, it must be used to perform match operations on the server name. */ ! boolean isMatched(Collection<SNIMatcher> matchers) { ! if (sniMap != null && !sniMap.isEmpty()) { for (SNIMatcher matcher : matchers) { ! SNIServerName sniName = sniMap.get(matcher.getType()); ! if (sniName != null && (!matcher.matches(sniName))) { ! return false; } } } ! return true; } ! /* ! * Is the extension is identical to a server name list? ! * ! * This method is used to check the server name indication during session ! * resumption. * ! * Per RFC 6066, when the server is deciding whether or not to accept a ! * request to resume a session, the contents of a server_name extension ! * MAY be used in the lookup of the session in the session cache. The ! * client SHOULD include the same server_name extension in the session ! * resumption request as it did in the full handshake that established ! * the session. A server that implements this extension MUST NOT accept ! * the request to resume the session if the server_name extension contains ! * a different name. Instead, it proceeds with a full handshake to ! * establish a new session. When resuming a session, the server MUST NOT ! * include a server_name extension in the server hello. */ ! boolean isIdentical(List<SNIServerName> other) { ! if (other.size() == sniMap.size()) { ! for(SNIServerName sniInOther : other) { ! SNIServerName sniName = sniMap.get(sniInOther.getType()); ! if (sniName == null || !sniInOther.equals(sniName)) { ! return false; ! } } ! return true; } ! return false; } @Override ! int length() { ! return listLength == 0 ? 4 : 6 + listLength; } @Override ! void send(HandshakeOutStream s) throws IOException { ! s.putInt16(type.id); ! if (listLength == 0) { ! s.putInt16(listLength); // in ServerHello, empty extension_data ! } else { ! s.putInt16(listLength + 2); // length of extension_data ! s.putInt16(listLength); // length of ServerNameList ! for (SNIServerName sniName : sniMap.values()) { ! s.putInt8(sniName.getType()); // server name type ! s.putBytes16(sniName.getEncoded()); // server name value } } } @Override ! public String toString() { ! StringBuilder sb = new StringBuilder(); ! for (SNIServerName sniName : sniMap.values()) { ! sb.append("[" + sniName + "]"); } ! return "Extension " + type + ", server_name: " + sb; } ! private static class UnknownServerName extends SNIServerName { ! UnknownServerName(int code, byte[] encoded) { ! super(code, encoded); } } } --- 24,586 ---- */ package sun.security.ssl; import java.io.IOException; + import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; + import java.util.List; import java.util.Map; ! import java.util.Objects; import javax.net.ssl.SNIHostName; import javax.net.ssl.SNIMatcher; import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLProtocolException; import javax.net.ssl.StandardConstants; + import static sun.security.ssl.SSLExtension.CH_SERVER_NAME; + import static sun.security.ssl.SSLExtension.EE_SERVER_NAME; + import sun.security.ssl.SSLExtension.ExtensionConsumer; + import static sun.security.ssl.SSLExtension.SH_SERVER_NAME; + import sun.security.ssl.SSLExtension.SSLExtensionSpec; + import sun.security.ssl.SSLHandshake.HandshakeMessage; ! /** ! * Pack of the "server_name" extensions [RFC 4366/6066]. */ ! final class ServerNameExtension { ! static final HandshakeProducer chNetworkProducer = ! new CHServerNameProducer(); ! static final ExtensionConsumer chOnLoadConcumer = ! new CHServerNameConsumer(); ! static final SSLStringize chStringize = ! new CHServerNamesStringize(); ! ! static final HandshakeProducer shNetworkProducer = ! new SHServerNameProducer(); ! static final ExtensionConsumer shOnLoadConcumer = ! new SHServerNameConsumer(); ! static final SSLStringize shStringize = ! new SHServerNamesStringize(); ! ! static final HandshakeProducer eeNetworkProducer = ! new EEServerNameProducer(); ! static final ExtensionConsumer eeOnLoadConcumer = ! new EEServerNameConsumer(); ! /** ! * The "server_name" extension. ! * ! * See RFC 4366/6066 for the specification of the extension. ! */ ! static final class CHServerNamesSpec implements SSLExtensionSpec { ! // For backward compatibility, all future data structures associated ! // with new NameTypes MUST begin with a 16-bit length field. ! static final int NAME_HEADER_LENGTH = 3; // 1: NameType ! // +2: Name length ! final List<SNIServerName> serverNames; ! ! private CHServerNamesSpec(List<SNIServerName> serverNames) { ! this.serverNames = ! Collections.<SNIServerName>unmodifiableList(serverNames); } ! private CHServerNamesSpec(ByteBuffer buffer) throws IOException { ! if (buffer.remaining() < 2) { ! throw new SSLProtocolException( ! "Invalid server_name extension: insufficient data"); } ! int sniLen = Record.getInt16(buffer); ! if ((sniLen == 0) || sniLen != buffer.remaining()) { throw new SSLProtocolException( ! "Invalid server_name extension: incomplete data"); } ! Map<Integer, SNIServerName> sniMap = new LinkedHashMap<>(); ! while (buffer.hasRemaining()) { ! int nameType = Record.getInt8(buffer); ! SNIServerName serverName; // HostName (length read in getBytes16); ! // ! // [RFC 6066] The data structure associated with the host_name ! // NameType is a variable-length vector that begins with a ! // 16-bit length. For backward compatibility, all future data ! // structures associated with new NameTypes MUST begin with a ! // 16-bit length field. TLS MAY treat provided server names as ! // opaque data and pass the names and types to the application. ! byte[] encoded = Record.getBytes16(buffer); ! if (nameType == StandardConstants.SNI_HOST_NAME) { if (encoded.length == 0) { throw new SSLProtocolException( ! "Empty HostName in server_name extension"); } + try { serverName = new SNIHostName(encoded); } catch (IllegalArgumentException iae) { SSLProtocolException spe = new SSLProtocolException( "Illegal server name, type=host_name(" + ! nameType + "), name=" + (new String(encoded, StandardCharsets.UTF_8)) + ! ", value={" + ! Utilities.toHexString(encoded) + "}"); ! throw (SSLProtocolException)spe.initCause(iae); } ! } else { try { ! serverName = new UnknownServerName(nameType, encoded); } catch (IllegalArgumentException iae) { SSLProtocolException spe = new SSLProtocolException( ! "Illegal server name, type=(" + nameType + ! "), value={" + ! Utilities.toHexString(encoded) + "}"); ! throw (SSLProtocolException)spe.initCause(iae); } } + // check for duplicated server name type if (sniMap.put(serverName.getType(), serverName) != null) { throw new SSLProtocolException( "Duplicated server name of type " + serverName.getType()); } + } ! this.serverNames = new ArrayList<>(sniMap.values()); } ! ! @Override ! public String toString() { ! if (serverNames == null || serverNames.isEmpty()) { ! return "<no server name indicator specified>"; ! } else { ! StringBuilder builder = new StringBuilder(512); ! for (SNIServerName sn : serverNames) { ! builder.append(sn.toString()); ! builder.append("\n"); } ! return builder.toString(); } } ! private static class UnknownServerName extends SNIServerName { ! UnknownServerName(int code, byte[] encoded) { ! super(code, encoded); ! } ! } } ! private static final class CHServerNamesStringize implements SSLStringize { ! @Override ! public String toString(ByteBuffer buffer) { ! try { ! return (new CHServerNamesSpec(buffer)).toString(); ! } catch (IOException ioe) { ! // For debug logging only, so please swallow exceptions. ! return ioe.getMessage(); ! } ! } } ! /** ! * Network data producer of a "server_name" extension in the ! * ClientHello handshake message. */ ! private static final ! class CHServerNameProducer implements HandshakeProducer { ! // Prevent instantiation of this class. ! private CHServerNameProducer() { ! // blank ! } ! ! @Override ! public byte[] produce(ConnectionContext context, ! HandshakeMessage message) throws IOException { ! // The producing happens in client side only. ! ClientHandshakeContext chc = (ClientHandshakeContext)context; ! ! // Is it a supported and enabled extension? ! if (!chc.sslConfig.isAvailable(CH_SERVER_NAME)) { ! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { ! SSLLogger.warning( ! "Ignore unavailable server_name extension"); ! } ! return null; ! } ! ! // Produce the extension. ! List<SNIServerName> serverNames; ! if (chc.isResumption && (chc.resumingSession != null)) { ! serverNames = ! chc.resumingSession.getRequestedServerNames(); ! } else { ! serverNames = chc.sslConfig.serverNames; ! } // Shall we use host too? ! ! // Empty server name list is not allowed in client mode. ! if ((serverNames != null) && !serverNames.isEmpty()) { ! int sniLen = 0; ! for (SNIServerName sniName : serverNames) { ! // For backward compatibility, all future data structures ! // associated with new NameTypes MUST begin with a 16-bit ! // length field. The header length of server name is 3 ! // bytes, including 1 byte NameType, and 2 bytes length ! // of the name. ! sniLen += CHServerNamesSpec.NAME_HEADER_LENGTH; ! sniLen += sniName.getEncoded().length; ! } ! ! byte[] extData = new byte[sniLen + 2]; ! ByteBuffer m = ByteBuffer.wrap(extData); ! Record.putInt16(m, sniLen); ! for (SNIServerName sniName : serverNames) { ! Record.putInt8(m, sniName.getType()); ! Record.putBytes16(m, sniName.getEncoded()); ! } ! ! // Update the context. ! chc.requestedServerNames = serverNames; ! chc.handshakeExtensions.put(CH_SERVER_NAME, ! new CHServerNamesSpec(serverNames)); ! ! return extData; ! } ! ! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { ! SSLLogger.warning("Unable to indicate server name"); ! } ! return null; ! } ! } ! ! /** ! * Network data consumer of a "server_name" extension in the ! * ClientHello handshake message. ! */ ! private static final ! class CHServerNameConsumer implements ExtensionConsumer { ! // Prevent instantiation of this class. ! private CHServerNameConsumer() { ! // blank ! } ! ! @Override ! public void consume(ConnectionContext context, ! HandshakeMessage message, ByteBuffer buffer) throws IOException { ! // The comsuming happens in server side only. ! ServerHandshakeContext shc = (ServerHandshakeContext)context; ! ! // Is it a supported and enabled extension? ! if (!shc.sslConfig.isAvailable(CH_SERVER_NAME)) { ! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { ! SSLLogger.fine( ! "Ignore unavailable extension: " + CH_SERVER_NAME.name); ! } ! return; // ignore the extension ! } ! ! // Parse the extension. ! CHServerNamesSpec spec; ! try { ! spec = new CHServerNamesSpec(buffer); ! } catch (IOException ioe) { ! shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); ! return; // fatal() always throws, make the compiler happy. ! } ! ! // Update the context. ! shc.handshakeExtensions.put(CH_SERVER_NAME, spec); ! ! // Does the server match the server name request? ! SNIServerName sni = null; ! if (!shc.sslConfig.sniMatchers.isEmpty()) { ! sni = chooseSni(shc.sslConfig.sniMatchers, spec.serverNames); ! if (sni != null) { ! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { ! SSLLogger.fine( ! "server name indication (" + ! sni + ") is accepted"); ! } ! } else { ! // We do not reject client without SNI extension currently. ! shc.conContext.fatal(Alert.UNRECOGNIZED_NAME, ! "Unrecognized server name indication"); ! } ! } else { ! // Note: Servers MAY require clients to send a valid ! // "server_name" extension and respond to a ClientHello ! // lacking a "server_name" extension by terminating the ! // connection with a "missing_extension" alert. ! // ! // We do not reject client without SNI extension currently. ! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { ! SSLLogger.fine( ! "no server name matchers, " + ! "ignore server name indication"); ! } ! } ! ! // Impact on session resumption. ! // ! // Does the resuming session have the same principal? ! if (shc.isResumption && shc.resumingSession != null) { ! // A server that implements this extension MUST NOT accept ! // the request to resume the session if the server_name ! // extension contains a different name. ! // ! // May only need to check that the session SNI is one of ! // the requested server names. ! if (!Objects.equals( ! sni, shc.resumingSession.serverNameIndication)) { ! shc.isResumption = false; ! shc.resumingSession = null; ! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { ! SSLLogger.fine( ! "abort session resumption, " + ! "different server name indication used"); ! } ! } ! } ! ! shc.requestedServerNames = spec.serverNames; ! shc.negotiatedServerName = sni; ! } ! ! private static SNIServerName chooseSni(Collection<SNIMatcher> matchers, ! List<SNIServerName> sniNames) { ! if (sniNames != null && !sniNames.isEmpty()) { for (SNIMatcher matcher : matchers) { ! int matcherType = matcher.getType(); ! for (SNIServerName sniName : sniNames) { ! if (sniName.getType() == matcherType) { ! if (matcher.matches(sniName)) { ! return sniName; ! } ! ! // no duplicated entry in the server names list. ! break; ! } } } } ! return null; ! } } ! /** ! * The "server_name" extension in the ServerHello handshake message. * ! * The "extension_data" field of this extension shall be empty. */ ! static final class SHServerNamesSpec implements SSLExtensionSpec { ! static final SHServerNamesSpec DEFAULT = new SHServerNamesSpec(); ! ! private SHServerNamesSpec() { ! // blank } ! private SHServerNamesSpec(ByteBuffer buffer) throws IOException { ! if (buffer.remaining() != 0) { ! throw new SSLProtocolException( ! "Invalid ServerHello server_name extension: not empty"); ! } } ! @Override ! public String toString() { ! return "<empty extension_data field>"; ! } } + private static final class SHServerNamesStringize implements SSLStringize { @Override ! public String toString(ByteBuffer buffer) { ! try { ! return (new SHServerNamesSpec(buffer)).toString(); ! } catch (IOException ioe) { ! // For debug logging only, so please swallow exceptions. ! return ioe.getMessage(); ! } ! } ! } ! ! /** ! * Network data producer of a "server_name" extension in the ! * ServerHello handshake message. ! */ ! private static final ! class SHServerNameProducer implements HandshakeProducer { ! // Prevent instantiation of this class. ! private SHServerNameProducer() { ! // blank } @Override ! public byte[] produce(ConnectionContext context, ! HandshakeMessage message) throws IOException { ! // The producing happens in server side only. ! ServerHandshakeContext shc = (ServerHandshakeContext)context; ! ! // In response to "server_name" extension request only ! CHServerNamesSpec spec = (CHServerNamesSpec) ! shc.handshakeExtensions.get(CH_SERVER_NAME); ! if (spec == null) { ! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { ! SSLLogger.finest( ! "Ignore unavailable extension: " + SH_SERVER_NAME.name); ! } ! return null; // ignore the extension ! } ! ! // When resuming a session, the server MUST NOT include a ! // server_name extension in the server hello. ! if (shc.isResumption || shc.negotiatedServerName == null) { ! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { ! SSLLogger.finest( ! "No expected server name indication response"); ! } ! return null; // ignore the extension ! } ! ! // Produce the extension and update the context. ! shc.handshakeExtensions.put( ! SH_SERVER_NAME, SHServerNamesSpec.DEFAULT); ! return (new byte[0]); // the empty extension_data } } + + /** + * Network data consumer of a "server_name" extension in the + * ServerHello handshake message. + */ + private static final + class SHServerNameConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private SHServerNameConsumer() { + // blank } @Override ! public void consume(ConnectionContext context, ! HandshakeMessage message, ByteBuffer buffer) throws IOException { ! // The comsuming happens in client side only. ! ClientHandshakeContext chc = (ClientHandshakeContext)context; ! ! // In response to "server_name" extension request only ! CHServerNamesSpec spec = (CHServerNamesSpec) ! chc.handshakeExtensions.get(CH_SERVER_NAME); ! if (spec == null) { ! chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ! "Unexpected ServerHello server_name extension"); ! } ! ! // Parse the extension. ! if (buffer.remaining() != 0) { ! chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ! "Invalid ServerHello server_name extension"); ! } ! ! // Update the context. ! chc.handshakeExtensions.put( ! SH_SERVER_NAME, SHServerNamesSpec.DEFAULT); ! // The negotiated server name is unknown in client side. Just ! // use the first request name as the value is not actually used ! // in the current implementation. ! chc.negotiatedServerName = spec.serverNames.get(0); ! } } ! /** ! * Network data producer of a "server_name" extension in the ! * EncryptedExtensions handshake message. ! */ ! private static final ! class EEServerNameProducer implements HandshakeProducer { ! // Prevent instantiation of this class. ! private EEServerNameProducer() { ! // blank } ! @Override ! public byte[] produce(ConnectionContext context, ! HandshakeMessage message) throws IOException { ! // The producing happens in server side only. ! ServerHandshakeContext shc = (ServerHandshakeContext)context; ! ! // In response to "server_name" extension request only ! CHServerNamesSpec spec = (CHServerNamesSpec) ! shc.handshakeExtensions.get(CH_SERVER_NAME); ! if (spec == null) { ! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { ! SSLLogger.finest( ! "Ignore unavailable extension: " + EE_SERVER_NAME.name); ! } ! return null; // ignore the extension ! } ! ! // When resuming a session, the server MUST NOT include a ! // server_name extension in the server hello. ! if (shc.isResumption || shc.negotiatedServerName == null) { ! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { ! SSLLogger.finest( ! "No expected server name indication response"); } + return null; // ignore the extension } + // Produce the extension and update the context. + shc.handshakeExtensions.put( + EE_SERVER_NAME, SHServerNamesSpec.DEFAULT); + + return (new byte[0]); // the empty extension_data + } + } + + /** + * Network data consumer of a "server_name" extension in the + * EncryptedExtensions handshake message. + */ + private static final + class EEServerNameConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private EEServerNameConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message, ByteBuffer buffer) throws IOException { + // The comsuming happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + // In response to "server_name" extension request only + CHServerNamesSpec spec = (CHServerNamesSpec) + chc.handshakeExtensions.get(CH_SERVER_NAME); + if (spec == null) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected EncryptedExtensions server_name extension"); + } + + // Parse the extension. + if (buffer.remaining() != 0) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Invalid EncryptedExtensions server_name extension"); + } + + // Update the context. + chc.handshakeExtensions.put( + EE_SERVER_NAME, SHServerNamesSpec.DEFAULT); + // The negotiated server name is unknown in client side. Just + // use the first request name as the value is not actually used + // in the current implementation. + chc.negotiatedServerName = spec.serverNames.get(0); + } + } }
< prev index next >