All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.taotao.boot.ip2region.model.IpAddress Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2020-2030, Shuigedeng ([email protected] & https://blog.taotaocloud.top/).
 *
 * 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
 *
 *      https://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.taotao.boot.ip2region.model;

import java.util.Arrays;

/**
 * 基于 zxipdb-java 简化改
 *
 *
 */
public class IpAddress {
    private byte[] mIp;

    public IpAddress(String ip) {
        mIp = textToNumericFormatV4(ip);
        if (mIp == null) {
            mIp = textToNumericFormatV6(ip);
        }
        if (mIp == null) {
            throw new IllegalArgumentException("invalid ip address `" + ip + "`");
        }
    }

    private IpAddress(byte[] mIp) {
        this.mIp = mIp;
    }

    public static IpAddress fromBytesV6(byte[] b) {
        if (b.length == INADDR16SZ) {
            return new IpAddress(b);
        } else if (b.length < INADDR16SZ) {
            byte[] bs = new byte[INADDR16SZ];
            System.arraycopy(b, 0, bs, 0, b.length);
            return new IpAddress(bs);
        } else {
            throw new IllegalArgumentException("非法IP地址");
        }
    }

    public static IpAddress fromBytesV6LE(byte[] b) {
        byte[] be = bsWap(b);
        return fromBytesV6(be);
    }

    public byte[] getBytes() {
        return mIp;
    }

    @Override
    public String toString() {
        if (mIp.length == INADDR4SZ) {
            return numericToTextFormatV4(mIp);
        } else {
            return numericToTextFormatV6(mIp);
        }
    }

    public int compareTo(IpAddress anIp) {
        return Arrays.compareUnsigned(mIp, anIp.getBytes());
    }

    private static byte[] bsWap(byte[] b) {
        byte[] rev = new byte[b.length];
        for (int i = 0, j = b.length - 1; j >= 0; i++, j--) {
            rev[j] = b[i];
        }
        return rev;
    }

    private static final int INADDR4SZ = 4;
    private static final int INADDR16SZ = 16;
    private static final int INT16SZ = 2;

    /*
     * Converts IPv4 address in its textual presentation form
     * into its numeric binary form.
     *
     * @param src a String representing an IPv4 address in standard format
     * @return a byte array representing the IPv4 numeric address
     */
    private static byte[] textToNumericFormatV4(String src) {
        int len = src.length();
        if (len == 0 || len > 15) {
            return null;
        }
        if (src.indexOf('.') < 1) {
            return null;
        }
        byte[] res = new byte[INADDR4SZ];
        long tmpValue = 0;
        int currByte = 0;
        boolean newOctet = true;
        for (int i = 0; i < len; i++) {
            char c = src.charAt(i);
            if (c == '.') {
                if (newOctet || tmpValue < 0 || tmpValue > 0xff || currByte == 3) {
                    return null;
                }
                res[currByte++] = (byte) (tmpValue & 0xff);
                tmpValue = 0;
                newOctet = true;
            } else {
                int digit = Character.digit(c, 10);
                if (digit < 0) {
                    return null;
                }
                tmpValue *= 10;
                tmpValue += digit;
                newOctet = false;
            }
        }
        if (newOctet || tmpValue < 0 || tmpValue >= (1L << ((4 - currByte) * 8))) {
            return null;
        }
        switch (currByte) {
            case 0:
                res[0] = (byte) ((tmpValue >> 24) & 0xff);
            case 1:
                res[1] = (byte) ((tmpValue >> 16) & 0xff);
            case 2:
                res[2] = (byte) ((tmpValue >> 8) & 0xff);
            case 3:
                res[3] = (byte) ((tmpValue) & 0xff);
        }
        return res;
    }

    /*
     * Convert IPv6 presentation level address to network order binary form.
     * credit:
     *  Converted from C code from Solaris 8 (inet_pton)
     *
     * Any component of the string following a per-cent % is ignored.
     *
     * @param src a String representing an IPv6 address in textual format
     * @return a byte array representing the IPv6 numeric address
     */
    private byte[] textToNumericFormatV6(String src) {
        // Shortest valid string is "::", hence at least 2 chars
        if (src.length() < 2) {
            return null;
        }
        int colonp;
        char ch;
        boolean sawXdigit;
        int val;
        char[] srcb = src.toCharArray();
        byte[] dst = new byte[INADDR16SZ];

        int srcbLength = srcb.length;
        int pc = src.indexOf('%');
        if (pc == srcbLength - 1) {
            return null;
        }
        if (pc != -1) {
            srcbLength = pc;
        }
        colonp = -1;
        int i = 0;
        int j = 0;
        /* Leading :: requires some special handling. */
        if (srcb[i] == ':' && (srcb[++i] != ':')) {
            return null;
        }
        int curtok = i;
        sawXdigit = false;
        val = 0;
        while (i < srcbLength) {
            ch = srcb[i++];
            int chval = Character.digit(ch, 16);
            if (chval != -1) {
                val <<= 4;
                val |= chval;
                if (val > 0xffff) {
                    return null;
                }
                sawXdigit = true;
                continue;
            }
            if (ch == ':') {
                curtok = i;
                if (!sawXdigit) {
                    if (colonp != -1) {
                        return null;
                    }
                    colonp = j;
                    continue;
                } else if (i == srcbLength) {
                    return null;
                }
                if (j + INT16SZ > INADDR16SZ) {
                    return null;
                }
                dst[j++] = (byte) ((val >> 8) & 0xff);
                dst[j++] = (byte) (val & 0xff);
                sawXdigit = false;
                val = 0;
                continue;
            }
            if (ch == '.' && ((j + INADDR4SZ) <= INADDR16SZ)) {
                String ia4 = src.substring(curtok, srcbLength);
                /* check this IPv4 address has 3 dots, ie. A.B.C.D */
                int dotCount = 0;
                int index = 0;
                while ((index = ia4.indexOf('.', index)) != -1) {
                    dotCount++;
                    index++;
                }
                if (dotCount != 3) {
                    return null;
                }
                byte[] v4Addr = textToNumericFormatV4(ia4);
                if (v4Addr == null) {
                    return null;
                }
                for (int k = 0; k < INADDR4SZ; k++) {
                    dst[j++] = v4Addr[k];
                }
                sawXdigit = false;
                break;
            }
            return null;
        }
        if (sawXdigit) {
            if (j + INT16SZ > INADDR16SZ) return null;
            dst[j++] = (byte) ((val >> 8) & 0xff);
            dst[j++] = (byte) (val & 0xff);
        }

        if (colonp != -1) {
            int n = j - colonp;
            if (j == INADDR16SZ) {
                return null;
            }
            for (i = 1; i <= n; i++) {
                dst[INADDR16SZ - i] = dst[colonp + n - i];
                dst[colonp + n - i] = 0;
            }
            j = INADDR16SZ;
        }
        if (j != INADDR16SZ) {
            return null;
        }
        mIp = dst;
        return mIp;
    }

    /**
     * Converts IPv4 binary address into a string suitable for presentation.
     *
     * @param src a byte array representing an IPv4 numeric address
     * @return a String representing the IPv4 address in
     * textual representation format
     */
    private static String numericToTextFormatV4(byte[] src) {
        return (src[0] & 0xff) + "." + (src[1] & 0xff) + "." + (src[2] & 0xff) + "." + (src[3] & 0xff);
    }

    /**
     * Convert IPv6 binary address into presentation (printable) format.
     *
     * @param src a byte array representing the IPv6 numeric address
     * @return a String representing an IPv6 address in
     * textual representation format
     */
    private static String numericToTextFormatV6(byte[] src) {
        StringBuilder sb = new StringBuilder(39);
        for (int i = 0; i < (INADDR16SZ / INT16SZ); i++) {
            sb.append(Integer.toHexString(((src[i << 1] << 8) & 0xff00) | (src[(i << 1) + 1] & 0xff)));
            if (i < (INADDR16SZ / INT16SZ) - 1) {
                sb.append(':');
            }
        }
        return sb.toString();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy