cn.hutool.core.net.Ipv4Util Maven / Gradle / Ivy
package cn.hutool.core.net;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
/**
* IPV4地址工具类
*
* pr自:https://gitee.com/loolly/hutool/pulls/161
*
* @author ZhuKun
* @since 5.4.1
*/
public class Ipv4Util {
public static final String LOCAL_IP = "127.0.0.1";
/**
* IP段的分割符
*/
public static final String IP_SPLIT_MARK = "-";
/**
* IP与掩码的分割符
*/
public static final String IP_MASK_SPLIT_MARK = StrUtil.SLASH;
/**
* 最大掩码位
*/
public static final int IP_MASK_MAX = 32;
/**
* 格式化IP段
*
* @param ip IP地址
* @param mask 掩码
* @return 返回xxx.xxx.xxx.xxx/mask的格式
*/
public static String formatIpBlock(String ip, String mask) {
return ip + IP_MASK_SPLIT_MARK + getMaskBitByMask(mask);
}
/**
* 智能转换IP地址集合
*
* @param ipRange IP段,支持X.X.X.X-X.X.X.X或X.X.X.X/X
* @param isAll true:全量地址,false:可用地址;仅在ipRange为X.X.X.X/X时才生效
* @return IP集
*/
public static List list(String ipRange, boolean isAll) {
if (ipRange.contains(IP_SPLIT_MARK)) {
// X.X.X.X-X.X.X.X
final String[] range = StrUtil.splitToArray(ipRange, IP_SPLIT_MARK);
return list(range[0], range[1]);
} else if (ipRange.contains(IP_MASK_SPLIT_MARK)) {
// X.X.X.X/X
final String[] param = StrUtil.splitToArray(ipRange, IP_MASK_SPLIT_MARK);
return list(param[0], Integer.parseInt(param[1]), isAll);
} else {
return ListUtil.toList(ipRange);
}
}
/**
* 根据IP地址、子网掩码获取IP地址区间
*
* @param ip IP地址
* @param maskBit 掩码位,例如24、32
* @param isAll true:全量地址,false:可用地址
* @return 区间地址
*/
public static List list(String ip, int maskBit, boolean isAll) {
if (maskBit == IP_MASK_MAX) {
final List list = new ArrayList<>();
if (isAll) {
list.add(ip);
}
return list;
}
String startIp = getBeginIpStr(ip, maskBit);
String endIp = getEndIpStr(ip, maskBit);
if (isAll) {
return list(startIp, endIp);
}
int lastDotIndex = startIp.lastIndexOf(CharUtil.DOT) + 1;
startIp = StrUtil.subPre(startIp, lastDotIndex) +
(Integer.parseInt(Objects.requireNonNull(StrUtil.subSuf(startIp, lastDotIndex))) + 1);
lastDotIndex = endIp.lastIndexOf(CharUtil.DOT) + 1;
endIp = StrUtil.subPre(endIp, lastDotIndex) +
(Integer.parseInt(Objects.requireNonNull(StrUtil.subSuf(endIp, lastDotIndex))) - 1);
return list(startIp, endIp);
}
/**
* 得到IP地址区间
*
* @param ipFrom 开始IP
* @param ipTo 结束IP
* @return 区间地址
*/
public static List list(String ipFrom, String ipTo) {
// 确定ip数量
final int count = countByIpRange(ipFrom, ipTo);
final int[] from = Convert.convert(int[].class, StrUtil.splitToArray(ipFrom, CharUtil.DOT));
final int[] to = Convert.convert(int[].class, StrUtil.splitToArray(ipTo, CharUtil.DOT));
final List ips = new ArrayList<>(count);
// 是否是循环的第一个值
boolean aIsStart = true, bIsStart = true, cIsStart = true;
// 是否是循环的最后一个值
boolean aIsEnd, bIsEnd, cIsEnd;
// 循环的结束值
int aEnd = to[0], bEnd, cEnd, dEnd;
for (int a = from[0]; a <= aEnd; a++) {
aIsEnd = (a == aEnd);
// 本次循环的结束结束值
bEnd = aIsEnd ? to[1] : 255;
for (int b = (aIsStart ? from[1] : 0); b <= bEnd; b++) {
// 在上一个循环是最后值的基础上进行判断
bIsEnd = aIsEnd && (b == bEnd);
cEnd = bIsEnd ? to[2] : 255;
for (int c = (bIsStart ? from[2] : 0); c <= cEnd; c++) {
// 在之前循环是最后值的基础上进行判断
cIsEnd = bIsEnd && (c == cEnd);
dEnd = cIsEnd ? to[3] : 255;
for (int d = (cIsStart ? from[3] : 0); d <= dEnd; d++) {
ips.add(a + "." + b + "." + c + "." + d);
}
cIsStart = false;
}
bIsStart = false;
}
aIsStart = false;
}
return ips;
}
/**
* 根据long值获取ip v4地址:xx.xx.xx.xx
*
* @param longIP IP的long表示形式
* @return IP V4 地址
*/
public static String longToIpv4(long longIP) {
final StringBuilder sb = StrUtil.builder();
// 直接右移24位
sb.append(longIP >> 24 & 0xFF);
sb.append(CharUtil.DOT);
// 将高8位置0,然后右移16位
sb.append(longIP >> 16 & 0xFF);
sb.append(CharUtil.DOT);
sb.append(longIP >> 8 & 0xFF);
sb.append(CharUtil.DOT);
sb.append(longIP & 0xFF);
return sb.toString();
}
/**
* 根据ip地址(xxx.xxx.xxx.xxx)计算出long型的数据
* 方法别名:inet_aton
*
* @param strIP IP V4 地址
* @return long值
*/
public static long ipv4ToLong(String strIP) {
final Matcher matcher = PatternPool.IPV4.matcher(strIP);
if (matcher.matches()) {
return matchAddress(matcher);
}
// Validator.validateIpv4(strIP, "Invalid IPv4 address!");
// final long[] ip = Convert.convert(long[].class, StrUtil.split(strIP, CharUtil.DOT));
// return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];
throw new IllegalArgumentException("Invalid IPv4 address!");
}
/**
* 根据ip地址(xxx.xxx.xxx.xxx)计算出long型的数据, 如果格式不正确返回 defaultValue
* @param strIP IP V4 地址
* @param defaultValue 默认值
* @return long值
*/
public static long ipv4ToLong(String strIP, long defaultValue) {
return Validator.isIpv4(strIP) ? ipv4ToLong(strIP) : defaultValue;
}
/**
* 根据 ip/掩码位 计算IP段的起始IP(字符串型)
* 方法别名:inet_ntoa
*
* @param ip 给定的IP,如218.240.38.69
* @param maskBit 给定的掩码位,如30
* @return 起始IP的字符串表示
*/
public static String getBeginIpStr(String ip, int maskBit) {
return longToIpv4(getBeginIpLong(ip, maskBit));
}
/**
* 根据 ip/掩码位 计算IP段的起始IP(Long型)
*
* @param ip 给定的IP,如218.240.38.69
* @param maskBit 给定的掩码位,如30
* @return 起始IP的长整型表示
*/
public static Long getBeginIpLong(String ip, int maskBit) {
return ipv4ToLong(ip) & ipv4ToLong(getMaskByMaskBit(maskBit));
}
/**
* 根据 ip/掩码位 计算IP段的终止IP(字符串型)
*
* @param ip 给定的IP,如218.240.38.69
* @param maskBit 给定的掩码位,如30
* @return 终止IP的字符串表示
*/
public static String getEndIpStr(String ip, int maskBit) {
return longToIpv4(getEndIpLong(ip, maskBit));
}
/**
* 根据子网掩码转换为掩码位
*
* @param mask 掩码的点分十进制表示,例如 255.255.255.0
* @return 掩码位,例如 24
* @throws IllegalArgumentException 子网掩码非法
*/
public static int getMaskBitByMask(String mask) {
Integer maskBit = MaskBit.getMaskBit(mask);
if (maskBit == null) {
throw new IllegalArgumentException("Invalid netmask " + mask);
}
return maskBit;
}
/**
* 计算子网大小
*
* @param maskBit 掩码位
* @param isAll true:全量地址,false:可用地址
* @return 地址总数
*/
public static int countByMaskBit(int maskBit, boolean isAll) {
//如果是可用地址的情况,掩码位小于等于0或大于等于32,则可用地址为0
if ((false == isAll) && (maskBit <= 0 || maskBit >= 32)) {
return 0;
}
final int count = (int) Math.pow(2, 32 - maskBit);
return isAll ? count : count - 2;
}
/**
* 根据掩码位获取掩码
*
* @param maskBit 掩码位
* @return 掩码
*/
public static String getMaskByMaskBit(int maskBit) {
return MaskBit.get(maskBit);
}
/**
* 根据开始IP与结束IP计算掩码
*
* @param fromIp 开始IP
* @param toIp 结束IP
* @return 掩码x.x.x.x
*/
public static String getMaskByIpRange(String fromIp, String toIp) {
long toIpLong = ipv4ToLong(toIp);
long fromIpLong = ipv4ToLong(fromIp);
Assert.isTrue(fromIpLong < toIpLong, "to IP must be greater than from IP!");
String[] fromIpSplit = StrUtil.splitToArray(fromIp, CharUtil.DOT);
String[] toIpSplit = StrUtil.splitToArray(toIp, CharUtil.DOT);
StringBuilder mask = new StringBuilder();
for (int i = 0; i < toIpSplit.length; i++) {
mask.append(255 - Integer.parseInt(toIpSplit[i]) + Integer.parseInt(fromIpSplit[i])).append(CharUtil.DOT);
}
return mask.substring(0, mask.length() - 1);
}
/**
* 计算IP区间有多少个IP
*
* @param fromIp 开始IP
* @param toIp 结束IP
* @return IP数量
*/
public static int countByIpRange(String fromIp, String toIp) {
long toIpLong = ipv4ToLong(toIp);
long fromIpLong = ipv4ToLong(fromIp);
if (fromIpLong > toIpLong) {
throw new IllegalArgumentException("to IP must be greater than from IP!");
}
int count = 1;
int[] fromIpSplit = StrUtil.split(fromIp, CharUtil.DOT).stream().mapToInt(Integer::parseInt).toArray();
int[] toIpSplit = StrUtil.split(toIp, CharUtil.DOT).stream().mapToInt(Integer::parseInt).toArray();
for (int i = fromIpSplit.length - 1; i >= 0; i--) {
count += (toIpSplit[i] - fromIpSplit[i]) * Math.pow(256, fromIpSplit.length - i - 1);
}
return count;
}
/**
* 判断掩码是否合法
*
* @param mask 掩码的点分十进制表示,例如 255.255.255.0
* @return true:掩码合法;false:掩码不合法
*/
public static boolean isMaskValid(String mask) {
return MaskBit.getMaskBit(mask) != null;
}
/**
* 判断掩码位是否合法
*
* @param maskBit 掩码位,例如 24
* @return true:掩码位合法;false:掩码位不合法
*/
public static boolean isMaskBitValid(int maskBit) {
return MaskBit.get(maskBit) != null;
}
/**
* 判定是否为内网IPv4
* 私有IP:
*
* A类 10.0.0.0-10.255.255.255
* B类 172.16.0.0-172.31.255.255
* C类 192.168.0.0-192.168.255.255
*
* 当然,还有127这个网段是环回地址
*
* @param ipAddress IP地址
* @return 是否为内网IP
* @since 5.7.18
*/
public static boolean isInnerIP(String ipAddress) {
boolean isInnerIp;
long ipNum = ipv4ToLong(ipAddress);
long aBegin = ipv4ToLong("10.0.0.0");
long aEnd = ipv4ToLong("10.255.255.255");
long bBegin = ipv4ToLong("172.16.0.0");
long bEnd = ipv4ToLong("172.31.255.255");
long cBegin = ipv4ToLong("192.168.0.0");
long cEnd = ipv4ToLong("192.168.255.255");
isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd) || LOCAL_IP.equals(ipAddress);
return isInnerIp;
}
/**
* 检测指定 IP 地址是否匹配通配符 wildcard
*
* @param wildcard 通配符,如 192.168.*.1
* @param ipAddress 待检测的 IP 地址
* @return 是否匹配
*/
public static boolean matches(String wildcard, String ipAddress) {
if (false == ReUtil.isMatch(PatternPool.IPV4, ipAddress)) {
return false;
}
final String[] wildcardSegments = wildcard.split("\\.");
final String[] ipSegments = ipAddress.split("\\.");
if (wildcardSegments.length != ipSegments.length) {
return false;
}
for (int i = 0; i < wildcardSegments.length; i++) {
if (false == "*".equals(wildcardSegments[i])
&& false == wildcardSegments[i].equals(ipSegments[i])) {
return false;
}
}
return true;
}
//-------------------------------------------------------------------------------- Private method start
/**
* 根据 ip/掩码位 计算IP段的终止IP(Long型)
* 注:此接口返回负数,请使用转成字符串后再转Long型
*
* @param ip 给定的IP,如218.240.38.69
* @param maskBit 给定的掩码位,如30
* @return 终止IP的长整型表示
*/
public static Long getEndIpLong(String ip, int maskBit) {
return getBeginIpLong(ip, maskBit)
+ ~ipv4ToLong(getMaskByMaskBit(maskBit));
}
/**
* 将匹配到的Ipv4地址的4个分组分别处理
*
* @param matcher 匹配到的Ipv4正则
* @return ipv4对应long
*/
private static long matchAddress(Matcher matcher) {
long addr = 0;
for (int i = 1; i <= 4; ++i) {
addr |= Long.parseLong(matcher.group(i)) << 8 * (4 - i);
}
return addr;
}
/**
* 指定IP的long是否在指定范围内
*
* @param userIp 用户IP
* @param begin 开始IP
* @param end 结束IP
* @return 是否在范围内
*/
private static boolean isInner(long userIp, long begin, long end) {
return (userIp >= begin) && (userIp <= end);
}
//-------------------------------------------------------------------------------- Private method end
}