src/java.base/share/classes/sun/security/ssl/ALPNExtension.java
changeset 50768 68fa3d4026ea
parent 50767 356eaea05bf0
child 50769 1bf8f9840705
equal deleted inserted replaced
50767:356eaea05bf0 50768:68fa3d4026ea
     1 /*
       
     2  * Copyright (c) 2015, 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 sun.security.ssl;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.nio.charset.*;
       
    30 import java.util.*;
       
    31 
       
    32 import javax.net.ssl.*;
       
    33 
       
    34 /*
       
    35  * [RFC 7301]
       
    36  * This TLS extension facilitates the negotiation of application-layer protocols
       
    37  * within the TLS handshake. Clients MAY include an extension of type
       
    38  * "application_layer_protocol_negotiation" in the (extended) ClientHello
       
    39  * message. The "extension_data" field of this extension SHALL contain a
       
    40  * "ProtocolNameList" value:
       
    41  *
       
    42  *     enum {
       
    43  *         application_layer_protocol_negotiation(16), (65535)
       
    44  *     } ExtensionType;
       
    45  *
       
    46  *     opaque ProtocolName<1..2^8-1>;
       
    47  *
       
    48  *     struct {
       
    49  *         ProtocolName protocol_name_list<2..2^16-1>
       
    50  *     } ProtocolNameList;
       
    51  */
       
    52 final class ALPNExtension extends HelloExtension {
       
    53 
       
    54     final static int ALPN_HEADER_LENGTH = 1;
       
    55     final static int MAX_APPLICATION_PROTOCOL_LENGTH = 255;
       
    56     final static int MAX_APPLICATION_PROTOCOL_LIST_LENGTH = 65535;
       
    57     private int listLength = 0;     // ProtocolNameList length
       
    58     private List<String> protocolNames = null;
       
    59 
       
    60     // constructor for ServerHello
       
    61     ALPNExtension(String protocolName) throws SSLException {
       
    62         this(new String[]{ protocolName });
       
    63     }
       
    64 
       
    65     // constructor for ClientHello
       
    66     ALPNExtension(String[] protocolNames) throws SSLException {
       
    67         super(ExtensionType.EXT_ALPN);
       
    68         if (protocolNames.length == 0) { // never null, never empty
       
    69             throw new IllegalArgumentException(
       
    70                 "The list of application protocols cannot be empty");
       
    71         }
       
    72         this.protocolNames = Arrays.asList(protocolNames);
       
    73         for (String p : protocolNames) {
       
    74             int length = p.getBytes(StandardCharsets.UTF_8).length;
       
    75             if (length == 0) {
       
    76                 throw new SSLProtocolException(
       
    77                     "Application protocol name is empty");
       
    78             }
       
    79             if (length <= MAX_APPLICATION_PROTOCOL_LENGTH) {
       
    80                 listLength += length + ALPN_HEADER_LENGTH;
       
    81             } else {
       
    82                 throw new SSLProtocolException(
       
    83                     "Application protocol name is too long: " + p);
       
    84             }
       
    85             if (listLength > MAX_APPLICATION_PROTOCOL_LIST_LENGTH) {
       
    86                 throw new SSLProtocolException(
       
    87                     "Application protocol name list is too long");
       
    88             }
       
    89         }
       
    90     }
       
    91 
       
    92     // constructor for ServerHello for parsing ALPN extension
       
    93     ALPNExtension(HandshakeInStream s, int len) throws IOException {
       
    94         super(ExtensionType.EXT_ALPN);
       
    95 
       
    96         if (len >= 2) {
       
    97             listLength = s.getInt16(); // list length
       
    98             if (listLength < 2 || listLength + 2 != len) {
       
    99                 throw new SSLProtocolException(
       
   100                     "Invalid " + type + " extension: incorrect list length " +
       
   101                     "(length=" + listLength + ")");
       
   102             }
       
   103         } else {
       
   104             throw new SSLProtocolException(
       
   105                 "Invalid " + type + " extension: insufficient data " +
       
   106                 "(length=" + len + ")");
       
   107         }
       
   108 
       
   109         int remaining = listLength;
       
   110         this.protocolNames = new ArrayList<>();
       
   111         while (remaining > 0) {
       
   112             // opaque ProtocolName<1..2^8-1>; // RFC 7301
       
   113             byte[] bytes = s.getBytes8();
       
   114             if (bytes.length == 0) {
       
   115                 throw new SSLProtocolException("Invalid " + type +
       
   116                     " extension: empty application protocol name");
       
   117             }
       
   118             String p =
       
   119                 new String(bytes, StandardCharsets.UTF_8); // app protocol
       
   120             protocolNames.add(p);
       
   121             remaining -= bytes.length + ALPN_HEADER_LENGTH;
       
   122         }
       
   123 
       
   124         if (remaining != 0) {
       
   125             throw new SSLProtocolException(
       
   126                 "Invalid " + type + " extension: extra data " +
       
   127                 "(length=" + remaining + ")");
       
   128         }
       
   129     }
       
   130 
       
   131     List<String> getPeerAPs() {
       
   132         return protocolNames;
       
   133     }
       
   134 
       
   135     /*
       
   136      * Return the length in bytes, including extension type and length fields.
       
   137      */
       
   138     @Override
       
   139     int length() {
       
   140         return 6 + listLength;
       
   141     }
       
   142 
       
   143     @Override
       
   144     void send(HandshakeOutStream s) throws IOException {
       
   145         s.putInt16(type.id);
       
   146         s.putInt16(listLength + 2); // length of extension_data
       
   147         s.putInt16(listLength);     // length of ProtocolNameList
       
   148 
       
   149         for (String p : protocolNames) {
       
   150             s.putBytes8(p.getBytes(StandardCharsets.UTF_8));
       
   151         }
       
   152     }
       
   153 
       
   154     @Override
       
   155     public String toString() {
       
   156         StringBuilder sb = new StringBuilder();
       
   157         if (protocolNames == null || protocolNames.isEmpty()) {
       
   158             sb.append("<empty>");
       
   159         } else {
       
   160             for (String protocolName : protocolNames) {
       
   161                 sb.append("[" + protocolName + "]");
       
   162             }
       
   163         }
       
   164 
       
   165         return "Extension " + type +
       
   166             ", protocol names: " + sb;
       
   167     }
       
   168 }