Path: blob/master/src/java.base/share/classes/sun/net/util/IPAddressUtil.java
41159 views
/*1* Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.net.util;2627import java.io.IOException;28import java.io.UncheckedIOException;29import java.net.Inet6Address;30import java.net.InetAddress;31import java.net.InetSocketAddress;32import java.net.NetworkInterface;33import java.net.SocketException;34import java.net.URL;35import java.security.AccessController;36import java.security.PrivilegedExceptionAction;37import java.security.PrivilegedActionException;38import java.util.Arrays;39import java.util.List;40import java.util.concurrent.ConcurrentHashMap;4142public class IPAddressUtil {43private static final int INADDR4SZ = 4;44private static final int INADDR16SZ = 16;45private static final int INT16SZ = 2;4647/*48* Converts IPv4 address in its textual presentation form49* into its numeric binary form.50*51* @param src a String representing an IPv4 address in standard format52* @return a byte array representing the IPv4 numeric address53*/54@SuppressWarnings("fallthrough")55public static byte[] textToNumericFormatV4(String src)56{57byte[] res = new byte[INADDR4SZ];5859long tmpValue = 0;60int currByte = 0;61boolean newOctet = true;6263int len = src.length();64if (len == 0 || len > 15) {65return null;66}67/*68* When only one part is given, the value is stored directly in69* the network address without any byte rearrangement.70*71* When a two part address is supplied, the last part is72* interpreted as a 24-bit quantity and placed in the right73* most three bytes of the network address. This makes the74* two part address format convenient for specifying Class A75* network addresses as net.host.76*77* When a three part address is specified, the last part is78* interpreted as a 16-bit quantity and placed in the right79* most two bytes of the network address. This makes the80* three part address format convenient for specifying81* Class B net- work addresses as 128.net.host.82*83* When four parts are specified, each is interpreted as a84* byte of data and assigned, from left to right, to the85* four bytes of an IPv4 address.86*87* We determine and parse the leading parts, if any, as single88* byte values in one pass directly into the resulting byte[],89* then the remainder is treated as a 8-to-32-bit entity and90* translated into the remaining bytes in the array.91*/92for (int i = 0; i < len; i++) {93char c = src.charAt(i);94if (c == '.') {95if (newOctet || tmpValue < 0 || tmpValue > 0xff || currByte == 3) {96return null;97}98res[currByte++] = (byte) (tmpValue & 0xff);99tmpValue = 0;100newOctet = true;101} else {102int digit = Character.digit(c, 10);103if (digit < 0) {104return null;105}106tmpValue *= 10;107tmpValue += digit;108newOctet = false;109}110}111if (newOctet || tmpValue < 0 || tmpValue >= (1L << ((4 - currByte) * 8))) {112return null;113}114switch (currByte) {115case 0:116res[0] = (byte) ((tmpValue >> 24) & 0xff);117case 1:118res[1] = (byte) ((tmpValue >> 16) & 0xff);119case 2:120res[2] = (byte) ((tmpValue >> 8) & 0xff);121case 3:122res[3] = (byte) ((tmpValue >> 0) & 0xff);123}124return res;125}126127/*128* Convert IPv6 presentation level address to network order binary form.129* credit:130* Converted from C code from Solaris 8 (inet_pton)131*132* Any component of the string following a per-cent % is ignored.133*134* @param src a String representing an IPv6 address in textual format135* @return a byte array representing the IPv6 numeric address136*/137public static byte[] textToNumericFormatV6(String src)138{139// Shortest valid string is "::", hence at least 2 chars140if (src.length() < 2) {141return null;142}143144int colonp;145char ch;146boolean saw_xdigit;147int val;148char[] srcb = src.toCharArray();149byte[] dst = new byte[INADDR16SZ];150151int srcb_length = srcb.length;152int pc = src.indexOf ('%');153if (pc == srcb_length -1) {154return null;155}156157if (pc != -1) {158srcb_length = pc;159}160161colonp = -1;162int i = 0, j = 0;163/* Leading :: requires some special handling. */164if (srcb[i] == ':')165if (srcb[++i] != ':')166return null;167int curtok = i;168saw_xdigit = false;169val = 0;170while (i < srcb_length) {171ch = srcb[i++];172int chval = Character.digit(ch, 16);173if (chval != -1) {174val <<= 4;175val |= chval;176if (val > 0xffff)177return null;178saw_xdigit = true;179continue;180}181if (ch == ':') {182curtok = i;183if (!saw_xdigit) {184if (colonp != -1)185return null;186colonp = j;187continue;188} else if (i == srcb_length) {189return null;190}191if (j + INT16SZ > INADDR16SZ)192return null;193dst[j++] = (byte) ((val >> 8) & 0xff);194dst[j++] = (byte) (val & 0xff);195saw_xdigit = false;196val = 0;197continue;198}199if (ch == '.' && ((j + INADDR4SZ) <= INADDR16SZ)) {200String ia4 = src.substring(curtok, srcb_length);201/* check this IPv4 address has 3 dots, i.e. A.B.C.D */202int dot_count = 0, index=0;203while ((index = ia4.indexOf ('.', index)) != -1) {204dot_count ++;205index ++;206}207if (dot_count != 3) {208return null;209}210byte[] v4addr = textToNumericFormatV4(ia4);211if (v4addr == null) {212return null;213}214for (int k = 0; k < INADDR4SZ; k++) {215dst[j++] = v4addr[k];216}217saw_xdigit = false;218break; /* '\0' was seen by inet_pton4(). */219}220return null;221}222if (saw_xdigit) {223if (j + INT16SZ > INADDR16SZ)224return null;225dst[j++] = (byte) ((val >> 8) & 0xff);226dst[j++] = (byte) (val & 0xff);227}228229if (colonp != -1) {230int n = j - colonp;231232if (j == INADDR16SZ)233return null;234for (i = 1; i <= n; i++) {235dst[INADDR16SZ - i] = dst[colonp + n - i];236dst[colonp + n - i] = 0;237}238j = INADDR16SZ;239}240if (j != INADDR16SZ)241return null;242byte[] newdst = convertFromIPv4MappedAddress(dst);243if (newdst != null) {244return newdst;245} else {246return dst;247}248}249250/**251* @param src a String representing an IPv4 address in textual format252* @return a boolean indicating whether src is an IPv4 literal address253*/254public static boolean isIPv4LiteralAddress(String src) {255return textToNumericFormatV4(src) != null;256}257258/**259* @param src a String representing an IPv6 address in textual format260* @return a boolean indicating whether src is an IPv6 literal address261*/262public static boolean isIPv6LiteralAddress(String src) {263return textToNumericFormatV6(src) != null;264}265266/*267* Convert IPv4-Mapped address to IPv4 address. Both input and268* returned value are in network order binary form.269*270* @param src a String representing an IPv4-Mapped address in textual format271* @return a byte array representing the IPv4 numeric address272*/273public static byte[] convertFromIPv4MappedAddress(byte[] addr) {274if (isIPv4MappedAddress(addr)) {275byte[] newAddr = new byte[INADDR4SZ];276System.arraycopy(addr, 12, newAddr, 0, INADDR4SZ);277return newAddr;278}279return null;280}281282/**283* Utility routine to check if the InetAddress is an284* IPv4 mapped IPv6 address.285*286* @return a <code>boolean</code> indicating if the InetAddress is287* an IPv4 mapped IPv6 address; or false if address is IPv4 address.288*/289private static boolean isIPv4MappedAddress(byte[] addr) {290if (addr.length < INADDR16SZ) {291return false;292}293if ((addr[0] == 0x00) && (addr[1] == 0x00) &&294(addr[2] == 0x00) && (addr[3] == 0x00) &&295(addr[4] == 0x00) && (addr[5] == 0x00) &&296(addr[6] == 0x00) && (addr[7] == 0x00) &&297(addr[8] == 0x00) && (addr[9] == 0x00) &&298(addr[10] == (byte)0xff) &&299(addr[11] == (byte)0xff)) {300return true;301}302return false;303}304/**305* Mapping from unscoped local Inet(6)Address to the same address306* including the correct scope-id, determined from NetworkInterface.307*/308private static final ConcurrentHashMap<InetAddress,InetAddress>309cache = new ConcurrentHashMap<>();310311/**312* Returns a scoped version of the supplied local, link-local ipv6 address313* if that scope-id can be determined from local NetworkInterfaces.314* If the address already has a scope-id or if the address is not local, ipv6315* or link local, then the original address is returned.316*317* @param address318* @exception SocketException if the given ipv6 link local address is found319* on more than one local interface320* @return321*/322public static InetAddress toScopedAddress(InetAddress address)323throws SocketException {324325if (address instanceof Inet6Address && address.isLinkLocalAddress()326&& ((Inet6Address) address).getScopeId() == 0) {327328InetAddress cached = null;329try {330cached = cache.computeIfAbsent(address, k -> findScopedAddress(k));331} catch (UncheckedIOException e) {332throw (SocketException)e.getCause();333}334return cached != null ? cached : address;335} else {336return address;337}338}339340/**341* Same as above for InetSocketAddress342*/343public static InetSocketAddress toScopedAddress(InetSocketAddress address)344throws SocketException {345InetAddress addr;346InetAddress orig = address.getAddress();347if ((addr = toScopedAddress(orig)) == orig) {348return address;349} else {350return new InetSocketAddress(addr, address.getPort());351}352}353354@SuppressWarnings("removal")355private static InetAddress findScopedAddress(InetAddress address) {356PrivilegedExceptionAction<List<InetAddress>> pa = () -> NetworkInterface.networkInterfaces()357.flatMap(NetworkInterface::inetAddresses)358.filter(a -> (a instanceof Inet6Address)359&& address.equals(a)360&& ((Inet6Address) a).getScopeId() != 0)361.toList();362List<InetAddress> result;363try {364result = AccessController.doPrivileged(pa);365var sz = result.size();366if (sz == 0)367return null;368if (sz > 1)369throw new UncheckedIOException(new SocketException(370"Duplicate link local addresses: must specify scope-id"));371return result.get(0);372} catch (PrivilegedActionException pae) {373return null;374}375}376377// See java.net.URI for more details on how to generate these378// masks.379//380// square brackets381private static final long L_IPV6_DELIMS = 0x0L; // "[]"382private static final long H_IPV6_DELIMS = 0x28000000L; // "[]"383// RFC 3986 gen-delims384private static final long L_GEN_DELIMS = 0x8400800800000000L; // ":/?#[]@"385private static final long H_GEN_DELIMS = 0x28000001L; // ":/?#[]@"386// These gen-delims can appear in authority387private static final long L_AUTH_DELIMS = 0x400000000000000L; // "@[]:"388private static final long H_AUTH_DELIMS = 0x28000001L; // "@[]:"389// colon is allowed in userinfo390private static final long L_COLON = 0x400000000000000L; // ":"391private static final long H_COLON = 0x0L; // ":"392// slash should be encoded in authority393private static final long L_SLASH = 0x800000000000L; // "/"394private static final long H_SLASH = 0x0L; // "/"395// backslash should always be encoded396private static final long L_BACKSLASH = 0x0L; // "\"397private static final long H_BACKSLASH = 0x10000000L; // "\"398// ASCII chars 0-31 + 127 - various controls + CRLF + TAB399private static final long L_NON_PRINTABLE = 0xffffffffL;400private static final long H_NON_PRINTABLE = 0x8000000000000000L;401// All of the above402private static final long L_EXCLUDE = 0x84008008ffffffffL;403private static final long H_EXCLUDE = 0x8000000038000001L;404405private static final char[] OTHERS = {4068263,8264,8265,8448,8449,8453,8454,10868,40765109,65110,65119,65131,65283,65295,65306,65311,65312408};409410// Tell whether the given character is found by the given mask pair411public static boolean match(char c, long lowMask, long highMask) {412if (c < 64)413return ((1L << c) & lowMask) != 0;414if (c < 128)415return ((1L << (c - 64)) & highMask) != 0;416return false; // other non ASCII characters are not filtered417}418419// returns -1 if the string doesn't contain any characters420// from the mask, the index of the first such character found421// otherwise.422public static int scan(String s, long lowMask, long highMask) {423int i = -1, len;424if (s == null || (len = s.length()) == 0) return -1;425boolean match = false;426while (++i < len && !(match = match(s.charAt(i), lowMask, highMask)));427if (match) return i;428return -1;429}430431public static int scan(String s, long lowMask, long highMask, char[] others) {432int i = -1, len;433if (s == null || (len = s.length()) == 0) return -1;434boolean match = false;435char c, c0 = others[0];436while (++i < len && !(match = match((c=s.charAt(i)), lowMask, highMask))) {437if (c >= c0 && (Arrays.binarySearch(others, c) > -1)) {438match = true; break;439}440}441if (match) return i;442443return -1;444}445446private static String describeChar(char c) {447if (c < 32 || c == 127) {448if (c == '\n') return "LF";449if (c == '\r') return "CR";450return "control char (code=" + (int)c + ")";451}452if (c == '\\') return "'\\'";453return "'" + c + "'";454}455456private static String checkUserInfo(String str) {457// colon is permitted in user info458int index = scan(str, L_EXCLUDE & ~L_COLON,459H_EXCLUDE & ~H_COLON);460if (index >= 0) {461return "Illegal character found in user-info: "462+ describeChar(str.charAt(index));463}464return null;465}466467private static String checkHost(String str) {468int index;469if (str.startsWith("[") && str.endsWith("]")) {470str = str.substring(1, str.length() - 1);471if (isIPv6LiteralAddress(str)) {472index = str.indexOf('%');473if (index >= 0) {474index = scan(str = str.substring(index),475L_NON_PRINTABLE | L_IPV6_DELIMS,476H_NON_PRINTABLE | H_IPV6_DELIMS);477if (index >= 0) {478return "Illegal character found in IPv6 scoped address: "479+ describeChar(str.charAt(index));480}481}482return null;483}484return "Unrecognized IPv6 address format";485} else {486index = scan(str, L_EXCLUDE, H_EXCLUDE);487if (index >= 0) {488return "Illegal character found in host: "489+ describeChar(str.charAt(index));490}491}492return null;493}494495private static String checkAuth(String str) {496int index = scan(str,497L_EXCLUDE & ~L_AUTH_DELIMS,498H_EXCLUDE & ~H_AUTH_DELIMS);499if (index >= 0) {500return "Illegal character found in authority: "501+ describeChar(str.charAt(index));502}503return null;504}505506// check authority of hierarchical URL. Appropriate for507// HTTP-like protocol handlers508public static String checkAuthority(URL url) {509String s, u, h;510if (url == null) return null;511if ((s = checkUserInfo(u = url.getUserInfo())) != null) {512return s;513}514if ((s = checkHost(h = url.getHost())) != null) {515return s;516}517if (h == null && u == null) {518return checkAuth(url.getAuthority());519}520return null;521}522523// minimal syntax checks - deeper check may be performed524// by the appropriate protocol handler525public static String checkExternalForm(URL url) {526String s;527if (url == null) return null;528int index = scan(s = url.getUserInfo(),529L_NON_PRINTABLE | L_SLASH,530H_NON_PRINTABLE | H_SLASH);531if (index >= 0) {532return "Illegal character found in authority: "533+ describeChar(s.charAt(index));534}535if ((s = checkHostString(url.getHost())) != null) {536return s;537}538return null;539}540541public static String checkHostString(String host) {542if (host == null) return null;543int index = scan(host,544L_NON_PRINTABLE | L_SLASH,545H_NON_PRINTABLE | H_SLASH,546OTHERS);547if (index >= 0) {548return "Illegal character found in host: "549+ describeChar(host.charAt(index));550}551return null;552}553}554555556