content: /*
   * Copyright 1999-2018 Alibaba Group Holding Ltd.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.alibaba.druid.support.http.util;
  
  /**
   * This class represents an IP Range, which are represented by an IP address and and a subnet mask. The standards
   * describing modern routing protocols often refer to the extended-network-prefix-length rather than the subnet mask.
   * The prefix length is equal to the number of contiguous one-bits in the traditional subnet mask. This means that
   * specifying the network address 130.5.5.25 with a subnet mask of 255.255.255.0 can also be expressed as 130.5.5.25/24.
   * The prefix-length notation is more compact and easier to understand than writing out the mask in its traditional
   * dotted-decimal format.
   *
   * @author Marcel Dullaart
   * @version 1.0
   * @see IPAddress
   */
  public class IPRange {
      /**
       * IP address
       */
      private IPAddress ipAddress;
  
      /**
       * IP subnet mask
       */
      private IPAddress ipSubnetMask;
  
      /**
       * extended network prefix
       */
      private int extendedNetworkPrefix;
  
      public IPRange(String range) {
          parseRange(range);
      }
  
      // -------------------------------------------------------------------------
  
      /**
       * Return the encapsulated IP address.
       *
       * @return The IP address.
       */
      public final IPAddress getIPAddress() {
          return ipAddress;
      }
  
      // -------------------------------------------------------------------------
  
      /**
       * Return the encapsulated subnet mask
       *
       * @return The IP range's subnet mask.
       */
      public final IPAddress getIPSubnetMask() {
          return ipSubnetMask;
      }
  
      // -------------------------------------------------------------------------
  
      /**
       * Return the extended extended network prefix.
       *
       * @return Return the extended network prefix.
       */
      public final int getExtendedNetworkPrefix() {
          return extendedNetworkPrefix;
      }
  
      // -------------------------------------------------------------------------
  
      /**
       * Convert the IP Range into a string representation.
       *
       * @return Return the string representation of the IP Address following the common format xxx.xxx.xxx.xxx/xx (IP
       * address/extended network prefixs).
       */
      public String toString() {
          return ipAddress.toString() + "/" + extendedNetworkPrefix;
      }
  
      // -------------------------------------------------------------------------
  
      /**
       * Parse the IP range string representation.
       *
       * @param range String representation of the IP range.
       * @throws IllegalArgumentException Throws this exception if the specified range is not a valid IP network range.
       */
      final void parseRange(String range) {
          if (range == null) {
              throw new IllegalArgumentException("Invalid IP range");
          }
  
          int index = range.indexOf('/');
          String subnetStr = null;
          if (index == -1) {
              ipAddress = new IPAddress(range);
          } else {
              ipAddress = new IPAddress(range.substring(0, index));
              subnetStr = range.substring(index + 1);
          }
  
          // try to convert the remaining part of the range into a decimal
          // value.
          try {
              if (subnetStr != null) {
                  extendedNetworkPrefix = Integer.parseInt(subnetStr);
                  if ((extendedNetworkPrefix < 0) || (extendedNetworkPrefix > 32)) {
                      throw new IllegalArgumentException("Invalid IP range [" + range + "]");
                  }
                  ipSubnetMask = computeMaskFromNetworkPrefix(extendedNetworkPrefix);
              }
          } catch (NumberFormatException ex) {
              // the remaining part is not a valid decimal value.
              // Check if it's a decimal-dotted notation.
              ipSubnetMask = new IPAddress(subnetStr);
  
              // create the corresponding subnet decimal
              extendedNetworkPrefix = computeNetworkPrefixFromMask(ipSubnetMask);
              if (extendedNetworkPrefix == -1) {
                  throw new IllegalArgumentException("Invalid IP range [" + range + "]", ex);
              }
          }
      }
  
      // -------------------------------------------------------------------------
  
      /**
       * Compute the extended network prefix from the IP subnet mask.
       *
       * @param mask Reference to the subnet mask IP number.
       * @return Return the extended network prefix. Return -1 if the specified mask cannot be converted into a extended
       * prefix network.
       */
      private int computeNetworkPrefixFromMask(IPAddress mask) {
          int result = 0;
          int tmp = mask.getIPAddress();
  
          while ((tmp & 0x00000001) == 0x00000001) {
              result++;
              tmp = tmp >>> 1;
          }
  
          if (tmp != 0) {
              return -1;
          }
  
          return result;
      }
  
      public static String toDecimalString(String inBinaryIpAddress) {
          StringBuilder decimalIp = new StringBuilder();
          String[] binary = new String[4];
  
          for (int i = 0, c = 0; i < 32; i = i + 8, c++) {
              binary[c] = inBinaryIpAddress.substring(i, i + 8);
              int octet = Integer.parseInt(binary[c], 2);
              decimalIp.append(octet);
              if (c < 3) {
                  decimalIp.append('.');
              }
          }
          return decimalIp.toString();
      }
  
      // -------------------------------------------------------------------------
  
      /**
       * Convert a extended network prefix integer into an IP number.
       *
       * @param prefix The network prefix number.
       * @return Return the IP number corresponding to the extended network prefix.
       */
      private IPAddress computeMaskFromNetworkPrefix(int prefix) {
          /*
           * int subnet = 0; for (int i=0; i<prefix; i++) { subnet = subnet << 1; subnet += 1; }
           */
  
          StringBuilder str = new StringBuilder();
          for (int i = 0; i < 32; i++) {
              if (i < prefix) {
                  str.append("1");
              } else {
                  str.append("0");
              }
          }
  
          String decimalString = toDecimalString(str.toString());
          return new IPAddress(decimalString);
  
      }
  
      // -------------------------------------------------------------------------
  
      /**
       * Check if the specified IP address is in the encapsulated range.
       *
       * @param address The IP address to be tested.
       * @return Return <code>true</code> if the specified IP address is in the encapsulated IP range, otherwise return
       * <code>false</code>.
       */
      public boolean isIPAddressInRange(IPAddress address) {
          if (ipSubnetMask == null) {
              return this.ipAddress.equals(address);
          }
  
          int result1 = address.getIPAddress() & ipSubnetMask.getIPAddress();
          int result2 = ipAddress.getIPAddress() & ipSubnetMask.getIPAddress();
  
          return result1 == result2;
      }
  
  }
  