1 /*
2  * Copyright (c) 2012, 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 javax.net.ssl;
27 
28 import java.util.Arrays;
29 
30 /**
31  * Instances of this class represent a server name in a Server Name
32  * Indication (SNI) extension.
33  * <P>
34  * The SNI extension is a feature that extends the SSL/TLS protocols to
35  * indicate what server name the client is attempting to connect to during
36  * handshaking.  See section 3, "Server Name Indication", of <A
37  * HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>.
38  * <P>
39  * {@code SNIServerName} objects are immutable.  Subclasses should not provide
40  * methods that can change the state of an instance once it has been created.
41  *
42  * @see SSLParameters#getServerNames()
43  * @see SSLParameters#setServerNames(List)
44  *
45  * @since 1.8
46  */
47 public abstract class SNIServerName {
48 
49     // the type of the server name
50     private final int type;
51 
52     // the encoded value of the server name
53     private final byte[] encoded;
54 
55     // the hex digitals
56     private static final char[] HEXES = "0123456789ABCDEF".toCharArray();
57 
58     /**
59      * Creates an {@code SNIServerName} using the specified name type and
60      * encoded value.
61      * <P>
62      * Note that the {@code encoded} byte array is cloned to protect against
63      * subsequent modification.
64      *
65      * @param  type
66      *         the type of the server name
67      * @param  encoded
68      *         the encoded value of the server name
69      *
70      * @throws IllegalArgumentException if {@code type} is not in the range
71      *         of 0 to 255, inclusive.
72      * @throws NullPointerException if {@code encoded} is null
73      */
SNIServerName(int type, byte[] encoded)74     protected SNIServerName(int type, byte[] encoded) {
75         if (type < 0) {
76             throw new IllegalArgumentException(
77                 "Server name type cannot be less than zero");
78         } else if (type > 255) {
79             throw new IllegalArgumentException(
80                 "Server name type cannot be greater than 255");
81         }
82         this.type = type;
83 
84         if (encoded == null) {
85             throw new NullPointerException(
86                 "Server name encoded value cannot be null");
87         }
88         this.encoded = encoded.clone();
89     }
90 
91 
92     /**
93      * Returns the name type of this server name.
94      *
95      * @return the name type of this server name
96      */
getType()97     public final int getType() {
98         return type;
99     }
100 
101     /**
102      * Returns a copy of the encoded server name value of this server name.
103      *
104      * @return a copy of the encoded server name value of this server name
105      */
getEncoded()106     public final byte[] getEncoded() {
107         return encoded.clone();
108     }
109 
110     /**
111      * Indicates whether some other object is "equal to" this server name.
112      *
113      * @return true if, and only if, {@code other} is of the same class
114      *         of this object, and has the same name type and
115      *         encoded value as this server name.
116      */
117     @Override
equals(Object other)118     public boolean equals(Object other) {
119         if (this == other) {
120             return true;
121         }
122 
123         if (this.getClass() != other.getClass()) {
124             return false;
125         }
126 
127         SNIServerName that = (SNIServerName)other;
128         return (this.type == that.type) &&
129                     Arrays.equals(this.encoded, that.encoded);
130     }
131 
132     /**
133      * Returns a hash code value for this server name.
134      * <P>
135      * The hash code value is generated using the name type and encoded
136      * value of this server name.
137      *
138      * @return a hash code value for this server name.
139      */
140     @Override
hashCode()141     public int hashCode() {
142         int result = 17;    // 17/31: prime number to decrease collisions
143         result = 31 * result + type;
144         result = 31 * result + Arrays.hashCode(encoded);
145 
146         return result;
147     }
148 
149     /**
150      * Returns a string representation of this server name, including the server
151      * name type and the encoded server name value in this
152      * {@code SNIServerName} object.
153      * <P>
154      * The exact details of the representation are unspecified and subject
155      * to change, but the following may be regarded as typical:
156      * <pre>
157      *     "type={@literal <name type>}, value={@literal <name value>}"
158      * </pre>
159      * <P>
160      * In this class, the format of "{@literal <name type>}" is
161      * "[LITERAL] (INTEGER)", where the optional "LITERAL" is the literal
162      * name, and INTEGER is the integer value of the name type.  The format
163      * of "{@literal <name value>}" is "XX:...:XX", where "XX" is the
164      * hexadecimal digit representation of a byte value. For example, a
165      * returned value of an pseudo server name may look like:
166      * <pre>
167      *     "type=(31), value=77:77:77:2E:65:78:61:6D:70:6C:65:2E:63:6E"
168      * </pre>
169      * or
170      * <pre>
171      *     "type=host_name (0), value=77:77:77:2E:65:78:61:6D:70:6C:65:2E:63:6E"
172      * </pre>
173      *
174      * <P>
175      * Please NOTE that the exact details of the representation are unspecified
176      * and subject to change, and subclasses may override the method with
177      * their own formats.
178      *
179      * @return a string representation of this server name
180      */
181     @Override
toString()182     public String toString() {
183         if (type == StandardConstants.SNI_HOST_NAME) {
184             return "type=host_name (0), value=" + toHexString(encoded);
185         } else {
186             return "type=(" + type + "), value=" + toHexString(encoded);
187         }
188     }
189 
190     // convert byte array to hex string
toHexString(byte[] bytes)191     private static String toHexString(byte[] bytes) {
192         if (bytes.length == 0) {
193             return "(empty)";
194         }
195 
196         StringBuilder sb = new StringBuilder(bytes.length * 3 - 1);
197         boolean isInitial = true;
198         for (byte b : bytes) {
199             if (isInitial) {
200                 isInitial = false;
201             } else {
202                 sb.append(':');
203             }
204 
205             int k = b & 0xFF;
206             sb.append(HEXES[k >>> 4]);
207             sb.append(HEXES[k & 0xF]);
208         }
209 
210         return sb.toString();
211     }
212 }
213