1 /*
  2  * Copyright (c) 2012, 2019, 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.math.BigInteger;
 29 import java.util.*;
 30 import java.util.regex.Pattern;
 31 import javax.net.ssl.*;
 32 import sun.net.util.IPAddressUtil;
 33 import sun.security.action.GetPropertyAction;
 34 
 35 /**
 36  * A utility class to share the static methods.
 37  */
 38 final class Utilities {
 39     static final char[] hexDigits = "0123456789ABCDEF".toCharArray();
 40     private static final String indent = "  ";
 41     private static final Pattern lineBreakPatern =
 42                 Pattern.compile("\\r\\n|\\n|\\r");
 43 
 44     /**
 45      * Puts {@code hostname} into the {@code serverNames} list.
 46      * <P>
 47      * If the {@code serverNames} does not look like a legal FQDN, it will
 48      * not be put into the returned list.
 49      * <P>
 50      * Note that the returned list does not allow duplicated name type.
 51      *
 52      * @return a list of {@link SNIServerName}
 53      */
 54     static List<SNIServerName> addToSNIServerNameList(
 55             List<SNIServerName> serverNames, String hostname) {
 56 
 57         SNIHostName sniHostName = rawToSNIHostName(hostname);
 58         if (sniHostName == null) {
 59             return serverNames;
 60         }
 61 
 62         int size = serverNames.size();
 63         List<SNIServerName> sniList = (size != 0) ?
 64                 new ArrayList<>(serverNames) :
 65                 new ArrayList<>(1);
 66 
 67         boolean reset = false;
 68         for (int i = 0; i < size; i++) {
 69             SNIServerName serverName = sniList.get(i);
 70             if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
 71                 sniList.set(i, sniHostName);
 72                 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
 73                      SSLLogger.fine(
 74                         "the previous server name in SNI (" + serverName +
 75                         ") was replaced with (" + sniHostName + ")");
 76                 }
 77                 reset = true;
 78                 break;
 79             }
 80         }
 81 
 82         if (!reset) {
 83             sniList.add(sniHostName);
 84         }
 85 
 86         return Collections.<SNIServerName>unmodifiableList(sniList);
 87     }
 88 
 89     /**
 90      * Converts string hostname to {@code SNIHostName}.
 91      * <P>
 92      * Note that to check whether a hostname is a valid domain name, we cannot
 93      * use the hostname resolved from name services.  For virtual hosting,
 94      * multiple hostnames may be bound to the same IP address, so the hostname
 95      * resolved from name services is not always reliable.
 96      *
 97      * @param  hostname
 98      *         the raw hostname
 99      * @return an instance of {@link SNIHostName}, or null if the hostname does
100      *         not look like a FQDN
101      */
102     private static SNIHostName rawToSNIHostName(String hostname) {
103         SNIHostName sniHostName = null;
104         if (hostname != null && hostname.indexOf('.') > 0 &&
105                 !hostname.endsWith(".") &&
106                 !IPAddressUtil.isIPv4LiteralAddress(hostname) &&
107                 !IPAddressUtil.isIPv6LiteralAddress(hostname)) {
108 
109             try {
110                 sniHostName = new SNIHostName(hostname);
111             } catch (IllegalArgumentException iae) {
112                 // don't bother to handle illegal host_name
113                 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
114                      SSLLogger.fine(hostname + "\" " +
115                         "is not a legal HostName for  server name indication");
116                 }
117             }
118         }
119 
120         return sniHostName;
121     }
122 
123     /**
124      * Return the value of the boolean System property propName.
125      *
126      * Note use of privileged action. Do NOT make accessible to applications.
127      */
128     static boolean getBooleanProperty(String propName, boolean defaultValue) {
129         // if set, require value of either true or false
130         String b = GetPropertyAction.privilegedGetProperty(propName);
131         if (b == null) {
132             return defaultValue;
133         } else if (b.equalsIgnoreCase("false")) {
134             return false;
135         } else if (b.equalsIgnoreCase("true")) {
136             return true;
137         } else {
138             throw new RuntimeException("Value of " + propName
139                 + " must either be 'true' or 'false'");
140         }
141     }
142 
143     static String indent(String source) {
144         return Utilities.indent(source, indent);
145     }
146 
147     static String indent(String source, String prefix) {
148         StringBuilder builder = new StringBuilder();
149         if (source == null) {
150              builder.append("\n").append(prefix).append("<blank message>");
151         } else {
152             String[] lines = lineBreakPatern.split(source);
153             boolean isFirst = true;
154             for (String line : lines) {
155                 if (isFirst) {
156                     isFirst = false;
157                 } else {
158                     builder.append("\n");
159                 }
160                 builder.append(prefix).append(line);
161             }
162         }
163 
164         return builder.toString();
165     }
166 
167     static String toHexString(byte b) {
168         return String.valueOf(hexDigits[(b >> 4) & 0x0F]) +
169                 String.valueOf(hexDigits[b & 0x0F]);
170     }
171 
172     static String byte16HexString(int id) {
173         return "0x" +
174                 hexDigits[(id >> 12) & 0x0F] + hexDigits[(id >> 8) & 0x0F] +
175                 hexDigits[(id >> 4) & 0x0F] + hexDigits[id & 0x0F];
176     }
177 
178     static String toHexString(byte[] bytes) {
179         if (bytes == null || bytes.length == 0) {
180             return "";
181         }
182 
183         StringBuilder builder = new StringBuilder(bytes.length * 3);
184         boolean isFirst = true;
185         for (byte b : bytes) {
186             if (isFirst) {
187                 isFirst = false;
188             } else {
189                 builder.append(' ');
190             }
191 
192             builder.append(hexDigits[(b >> 4) & 0x0F]);
193             builder.append(hexDigits[b & 0x0F]);
194         }
195         return builder.toString();
196     }
197 
198     static String toHexString(long lv) {
199         StringBuilder builder = new StringBuilder(128);
200 
201         boolean isFirst = true;
202         do {
203             if (isFirst) {
204                 isFirst = false;
205             } else {
206                 builder.append(' ');
207             }
208 
209             builder.append(hexDigits[(int)(lv & 0x0F)]);
210             lv >>>= 4;
211             builder.append(hexDigits[(int)(lv & 0x0F)]);
212             lv >>>= 4;
213         } while (lv != 0);
214         builder.reverse();
215 
216         return builder.toString();
217     }
218 
219     /**
220      * Utility method to convert a BigInteger to a byte array in unsigned
221      * format as needed in the handshake messages. BigInteger uses
222      * 2's complement format, i.e. it prepends an extra zero if the MSB
223      * is set. We remove that.
224      */
225     static byte[] toByteArray(BigInteger bi) {
226         byte[] b = bi.toByteArray();
227         if ((b.length > 1) && (b[0] == 0)) {
228             int n = b.length - 1;
229             byte[] newarray = new byte[n];
230             System.arraycopy(b, 1, newarray, 0, n);
231             b = newarray;
232         }
233         return b;
234     }
235 
236     static void reverseBytes(byte[] arr) {
237         int i = 0;
238         int j = arr.length - 1;
239 
240         while (i < j) {
241             swap(arr, i, j);
242             i++;
243             j--;
244         }
245     }
246 
247     private static void swap(byte[] arr, int i, int j) {
248         byte tmp = arr[i];
249         arr[i] = arr[j];
250         arr[j] = tmp;
251     }
252 }