1 /*
   2  * Copyright (c) 2004, 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.net.util;
  27 
  28 public class IPAddressUtil {
  29     private static final int INADDR4SZ = 4;
  30     private static final int INADDR16SZ = 16;
  31     private static final int INT16SZ = 2;
  32 
  33     /*
  34      * Converts IPv4 address in its textual presentation form
  35      * into its numeric binary form.
  36      *
  37      * @param src a String representing an IPv4 address in standard format
  38      * @return a byte array representing the IPv4 numeric address
  39      */
  40     @SuppressWarnings("fallthrough")
  41     public static byte[] textToNumericFormatV4(String src)
  42     {
  43         byte[] res = new byte[INADDR4SZ];
  44 
  45         long tmpValue = 0;
  46         int currByte = 0;
  47         boolean newOctet = true;
  48 
  49         int len = src.length();
  50         if (len == 0 || len > 15) {
  51             return null;
  52         }
  53         /*
  54          * When only one part is given, the value is stored directly in
  55          * the network address without any byte rearrangement.
  56          *
  57          * When a two part address is supplied, the last part is
  58          * interpreted as a 24-bit quantity and placed in the right
  59          * most three bytes of the network address. This makes the
  60          * two part address format convenient for specifying Class A
  61          * network addresses as net.host.
  62          *
  63          * When a three part address is specified, the last part is
  64          * interpreted as a 16-bit quantity and placed in the right
  65          * most two bytes of the network address. This makes the
  66          * three part address format convenient for specifying
  67          * Class B net- work addresses as 128.net.host.
  68          *
  69          * When four parts are specified, each is interpreted as a
  70          * byte of data and assigned, from left to right, to the
  71          * four bytes of an IPv4 address.
  72          *
  73          * We determine and parse the leading parts, if any, as single
  74          * byte values in one pass directly into the resulting byte[],
  75          * then the remainder is treated as a 8-to-32-bit entity and
  76          * translated into the remaining bytes in the array.
  77          */
  78         for (int i = 0; i < len; i++) {
  79             char c = src.charAt(i);
  80             if (c == '.') {
  81                 if (newOctet || tmpValue < 0 || tmpValue > 0xff || currByte == 3) {
  82                     return null;
  83                 }
  84                 res[currByte++] = (byte) (tmpValue & 0xff);
  85                 tmpValue = 0;
  86                 newOctet = true;
  87             } else {
  88                 int digit = Character.digit(c, 10);
  89                 if (digit < 0) {
  90                     return null;
  91                 }
  92                 tmpValue *= 10;
  93                 tmpValue += digit;
  94                 newOctet = false;
  95             }
  96         }
  97         if (newOctet || tmpValue < 0 || tmpValue >= (1L << ((4 - currByte) * 8))) {
  98             return null;
  99         }
 100         switch (currByte) {
 101             case 0:
 102                 res[0] = (byte) ((tmpValue >> 24) & 0xff);
 103             case 1:
 104                 res[1] = (byte) ((tmpValue >> 16) & 0xff);
 105             case 2:
 106                 res[2] = (byte) ((tmpValue >>  8) & 0xff);
 107             case 3:
 108                 res[3] = (byte) ((tmpValue >>  0) & 0xff);
 109         }
 110         return res;
 111     }
 112 
 113     /*
 114      * Convert IPv6 presentation level address to network order binary form.
 115      * credit:
 116      *  Converted from C code from Solaris 8 (inet_pton)
 117      *
 118      * Any component of the string following a per-cent % is ignored.
 119      *
 120      * @param src a String representing an IPv6 address in textual format
 121      * @return a byte array representing the IPv6 numeric address
 122      */
 123     public static byte[] textToNumericFormatV6(String src)
 124     {
 125         // Shortest valid string is "::", hence at least 2 chars
 126         if (src.length() < 2) {
 127             return null;
 128         }
 129 
 130         int colonp;
 131         char ch;
 132         boolean saw_xdigit;
 133         int val;
 134         char[] srcb = src.toCharArray();
 135         byte[] dst = new byte[INADDR16SZ];
 136 
 137         int srcb_length = srcb.length;
 138         int pc = src.indexOf ('%');
 139         if (pc == srcb_length -1) {
 140             return null;
 141         }
 142 
 143         if (pc != -1) {
 144             srcb_length = pc;
 145         }
 146 
 147         colonp = -1;
 148         int i = 0, j = 0;
 149         /* Leading :: requires some special handling. */
 150         if (srcb[i] == ':')
 151             if (srcb[++i] != ':')
 152                 return null;
 153         int curtok = i;
 154         saw_xdigit = false;
 155         val = 0;
 156         while (i < srcb_length) {
 157             ch = srcb[i++];
 158             int chval = Character.digit(ch, 16);
 159             if (chval != -1) {
 160                 val <<= 4;
 161                 val |= chval;
 162                 if (val > 0xffff)
 163                     return null;
 164                 saw_xdigit = true;
 165                 continue;
 166             }
 167             if (ch == ':') {
 168                 curtok = i;
 169                 if (!saw_xdigit) {
 170                     if (colonp != -1)
 171                         return null;
 172                     colonp = j;
 173                     continue;
 174                 } else if (i == srcb_length) {
 175                     return null;
 176                 }
 177                 if (j + INT16SZ > INADDR16SZ)
 178                     return null;
 179                 dst[j++] = (byte) ((val >> 8) & 0xff);
 180                 dst[j++] = (byte) (val & 0xff);
 181                 saw_xdigit = false;
 182                 val = 0;
 183                 continue;
 184             }
 185             if (ch == '.' && ((j + INADDR4SZ) <= INADDR16SZ)) {
 186                 String ia4 = src.substring(curtok, srcb_length);
 187                 /* check this IPv4 address has 3 dots, i.e. A.B.C.D */
 188                 int dot_count = 0, index=0;
 189                 while ((index = ia4.indexOf ('.', index)) != -1) {
 190                     dot_count ++;
 191                     index ++;
 192                 }
 193                 if (dot_count != 3) {
 194                     return null;
 195                 }
 196                 byte[] v4addr = textToNumericFormatV4(ia4);
 197                 if (v4addr == null) {
 198                     return null;
 199                 }
 200                 for (int k = 0; k < INADDR4SZ; k++) {
 201                     dst[j++] = v4addr[k];
 202                 }
 203                 saw_xdigit = false;
 204                 break;  /* '\0' was seen by inet_pton4(). */
 205             }
 206             return null;
 207         }
 208         if (saw_xdigit) {
 209             if (j + INT16SZ > INADDR16SZ)
 210                 return null;
 211             dst[j++] = (byte) ((val >> 8) & 0xff);
 212             dst[j++] = (byte) (val & 0xff);
 213         }
 214 
 215         if (colonp != -1) {
 216             int n = j - colonp;
 217 
 218             if (j == INADDR16SZ)
 219                 return null;
 220             for (i = 1; i <= n; i++) {
 221                 dst[INADDR16SZ - i] = dst[colonp + n - i];
 222                 dst[colonp + n - i] = 0;
 223             }
 224             j = INADDR16SZ;
 225         }
 226         if (j != INADDR16SZ)
 227             return null;
 228         byte[] newdst = convertFromIPv4MappedAddress(dst);
 229         if (newdst != null) {
 230             return newdst;
 231         } else {
 232             return dst;
 233         }
 234     }
 235 
 236     /**
 237      * @param src a String representing an IPv4 address in textual format
 238      * @return a boolean indicating whether src is an IPv4 literal address
 239      */
 240     public static boolean isIPv4LiteralAddress(String src) {
 241         return textToNumericFormatV4(src) != null;
 242     }
 243 
 244     /**
 245      * @param src a String representing an IPv6 address in textual format
 246      * @return a boolean indicating whether src is an IPv6 literal address
 247      */
 248     public static boolean isIPv6LiteralAddress(String src) {
 249         return textToNumericFormatV6(src) != null;
 250     }
 251 
 252     /*
 253      * Convert IPv4-Mapped address to IPv4 address. Both input and
 254      * returned value are in network order binary form.
 255      *
 256      * @param src a String representing an IPv4-Mapped address in textual format
 257      * @return a byte array representing the IPv4 numeric address
 258      */
 259     public static byte[] convertFromIPv4MappedAddress(byte[] addr) {
 260         if (isIPv4MappedAddress(addr)) {
 261             byte[] newAddr = new byte[INADDR4SZ];
 262             System.arraycopy(addr, 12, newAddr, 0, INADDR4SZ);
 263             return newAddr;
 264         }
 265         return null;
 266     }
 267 
 268     /**
 269      * Utility routine to check if the InetAddress is an
 270      * IPv4 mapped IPv6 address.
 271      *
 272      * @return a <code>boolean</code> indicating if the InetAddress is
 273      * an IPv4 mapped IPv6 address; or false if address is IPv4 address.
 274      */
 275     private static boolean isIPv4MappedAddress(byte[] addr) {
 276         if (addr.length < INADDR16SZ) {
 277             return false;
 278         }
 279         if ((addr[0] == 0x00) && (addr[1] == 0x00) &&
 280             (addr[2] == 0x00) && (addr[3] == 0x00) &&
 281             (addr[4] == 0x00) && (addr[5] == 0x00) &&
 282             (addr[6] == 0x00) && (addr[7] == 0x00) &&
 283             (addr[8] == 0x00) && (addr[9] == 0x00) &&
 284             (addr[10] == (byte)0xff) &&
 285             (addr[11] == (byte)0xff))  {
 286             return true;
 287         }
 288         return false;
 289     }
 290 }