
org.joyqueue.toolkit.network.IpUtil Maven / Gradle / Ivy
/**
* Copyright 2019 The JoyQueue Authors.
*
* 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 org.joyqueue.toolkit.network;
import sun.net.util.IPAddressUtil;
import java.net.*;
import java.util.*;
/**
* Ipv4工具
*
* @author hexiaofeng
*/
public class IpUtil {
public static String IPV4_PORT_SEPARATOR = ":";
public static String IPV6_PORT_SEPARATOR = "_";
public static String IPV4_SEPARATOR = ".";
public static String IPV6_SEPARATOR = ":";
/**
* 管理IP
*/
public static String MANAGE_IP = "10.";
/**
* 网卡
*/
public static String NET_INTERFACE;
/**
* 内网地址
*/
public static final Lan INTRANET = new Lan("172.16.0.0/12;192.168.0.0/16;10.0.0.0/8");
/**
* 是否启用IPV6
*/
public static boolean PREFER_IPV6 = false;
static {
// 从环境变量里面获取默认的网卡和管理网络
NET_INTERFACE = System.getProperty("nic");
MANAGE_IP = System.getProperty("manage_ip", MANAGE_IP);
PREFER_IPV6 = Boolean.valueOf(System.getProperty("java.net.preferIPv6Stack", "false"));
}
/**
* 得到本机所有的地址
*
* @return 本机所有的地址
*/
public static List getLocalIps() {
return getLocalIps(null, null);
}
/**
* 得到指定网卡上的地址
*
* @param nic 网卡
* @param exclude 排除的地址
*/
public static List getLocalIps(final String nic, final String exclude) {
try {
List result = new ArrayList();
NetworkInterface ni;
Enumeration ias;
InetAddress address;
Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces();
while (netInterfaces.hasMoreElements()) {
ni = netInterfaces.nextElement();
if (nic != null && !nic.isEmpty() && !ni.getName().equals(nic)) {
continue;
}
ias = ni.getInetAddresses();
while (ias.hasMoreElements()) {
address = ias.nextElement();
if (!address.isLoopbackAddress() &&
//回送地址:它是分配给回送接口的地址
!address.isAnyLocalAddress() &&
//多播地址:也称为 Anylocal 地址或通配符地址
((address instanceof Inet4Address && !PREFER_IPV6) || (address instanceof Inet6Address && PREFER_IPV6))) {
result.add(address.getHostAddress());
}
}
}
// 只有一个IP
int count = result.size();
if (count <= 1) {
return result;
}
if (exclude != null && !exclude.isEmpty()) {
String ip;
// 多个IP,排除IP
for (int i = count - 1; i >= 0; i--) {
ip = result.get(i);
if (ip.startsWith(exclude)) {
// 删除排除的IP
result.remove(i);
count--;
if (count == 1) {
// 确保有一个IP
break;
}
}
}
}
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 得到本机内网地址
*
* @param nic 网卡
* @param manageIp 管理段IP地址
* @return 本机地址
*/
public static String getLocalIp(final String nic, final String manageIp) {
List ips = getLocalIps(nic, manageIp);
if (ips != null && !ips.isEmpty()) {
if (ips.size() == 1) {
return ips.get(0);
}
if (!PREFER_IPV6) {
for (String ip : ips) {
if (INTRANET.contains(ip)) {
return ip;
}
}
}
return ips.get(0);
}
return null;
}
/**
* 得到本机内网地址
*
* @param manageIp 管理段IP地址
* @return 本机地址
*/
public static String getLocalIp(final String manageIp) {
return getLocalIp(NET_INTERFACE, manageIp);
}
/**
* 得到本机内网地址
*
* @return 本机地址
*/
public static String getLocalIp() {
// In JD.com IDC, this should be:
// return getLocalIp(NET_INTERFACE, MANAGE_IP);
return getDefaultLocalIp();
}
private static String getDefaultLocalIp() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
List ips = getLocalIps();
if(null != ips && !ips.isEmpty()){
return ips.get(0);
}
return "127.0.0.1";
}
}
/**
* 把地址对象转换成字符串
*
* @param address 地址
* @return 地址字符串
*/
//TODO
public static String toAddress(final SocketAddress address) {
if (address == null) {
return null;
}
if (address instanceof InetSocketAddress) {
InetSocketAddress isa = (InetSocketAddress) address;
StringBuilder builder = new StringBuilder(50);
if (address instanceof InetSocketAddress) {
builder.append(isa.getAddress().getHostAddress());
String separator = isValidIpV4Address(((InetSocketAddress) address).getHostString()) ? IPV4_PORT_SEPARATOR : IPV6_PORT_SEPARATOR;
builder.append(separator).append(isa.getPort());
}
return builder.toString();
} else {
return address.toString();
}
}
/**
* 从地址数组中获取IP,跳过必要的端口
*
* @param address 地址数组
* @return IP
*/
public static String toIp(final byte[] address) {
if (address == null || address.length < 4) {
return null;
}
int pos = 0;
if (address.length == 6 || address.length == 18) {
// 跳过端口
pos += 2;
}
StringBuilder builder = new StringBuilder();
for (int i = pos; i < address.length - 1; i++) {
if (address.length <= 6) {
builder.append(address[pos++] & 0xFF).append(IPV4_SEPARATOR);
} else {
builder = new StringBuilder(20);
builder.append(address[pos++] & 0xFF).append(IPV6_SEPARATOR);
}
}
builder.append(address[pos++] & 0xFF);
return builder.toString();
}
/**
* 把地址转化成字节数组,如果有端口,则第一和第二字节为端口,其余为IP段
*
*
* 解析地址
,分隔符支持".",":","_"
* IPV4地址第1-4个元素为IP段,第5个元素为端口,如果第5个元素为-1,则表示端口不存在
* IPV6地址第1-8个元素为IP段,第9个元素为端口,如果第9个元素为-1,则表示端口不存在
*
* @param address 地址
* @return 字节数组
*/
public static byte[] toByte(String address) {
if (address == null || address.isEmpty()) {
return null;
}
String upperAddress = address.toUpperCase();
StringBuilder host = new StringBuilder();
char[] chars = upperAddress.toCharArray();
char ch;
int start = -1;
int end = -1;
int index = 0;
char sep = ' ';
for (int i = 0; i < chars.length; i++) {
ch = chars[i];
switch (ch) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
if (start == -1) {
start = i;
}
end = i;
break;
case '_':
case ':':
case '.':
index++;
// 分隔符
if (start == -1) {
// 前面必须有字符
return null;
}
if (start != 0) {
host.append(sep);
}
host.append(new String(chars, start, end - start + 1));
sep = ch;
start = -1;
end = -1;
break;
default:
return null;
}
}
String last = new String(chars, start, end - start + 1);
if (index == 3 || index == 7) {
host.append(String.valueOf(sep)).append(last);
return toByteWithoutPort(host.toString());
} else {
byte[] data = toByteWithoutPort(host.toString());
int port = Integer.valueOf(new String(chars, start, end - start + 1));
byte[] result = new byte[data.length + 2];
System.arraycopy(data, 0, result, 2, data.length);
result[1] = (byte) (port >> 8 & 0xFF);
result[0] = (byte) (port & 0xFF);
return result;
}
}
/**
* 地址转化成字节数组
*
* @param socketAddress 地址对象
* @return 字节数组
*/
public static byte[] toByte(InetSocketAddress socketAddress) {
if (socketAddress == null) {
throw new IllegalArgumentException("socketAddress is null");
}
InetAddress inetAddress = socketAddress.getAddress();
if (inetAddress == null) {
throw new IllegalArgumentException("socketAddress is invalid");
}
byte[] address = inetAddress.getAddress();
byte[] result = new byte[address.length + 2];
System.arraycopy(address, 0, result, 2, address.length);
int port = socketAddress.getPort();
result[1] = (byte) (port >> 8 & 0xFF);
result[0] = (byte) (port & 0xFF);
return result;
}
/**
* 把字节数组转换成地址对象
*
* @param address 地址字节数组
* @return 地址对象
*/
public static InetSocketAddress toAddress(final byte[] address) {
if (address == null || (address.length != 6 && address.length != 18)) {
// 端口2个字节,IPV4 4字节,IPV6 16字节
throw new IllegalArgumentException("address is invalid");
}
// 低位2个字节是端口数据
int port = address[0] & 0xFF;
port |= (address[1] << 8 & 0xFF00);
try {
InetAddress addr = InetAddress.getByAddress(null, Arrays.copyOfRange(address, 2, address.length));
return new InetSocketAddress(addr, port);
} catch (UnknownHostException ignored) {
return null;
}
}
/**
* 把地址数组转换成字符串
*
* @param address 字节数组
* @param builder 字符串构造器
*/
public static void toAddress(byte[] address, StringBuilder builder) {
if (builder == null) {
return;
}
if (address == null) {
throw new IllegalArgumentException("address is invalid");
}
if (address.length < 4) {
throw new IllegalArgumentException("address is invalid");
}
int pos = 0;
int port = 0;
if (address.length >= 6) {
port = address[pos++] & 0xFF;
port |= (address[pos++] << 8 & 0xFF00);
}
builder.append(address[pos++] & 0xFF).append('.');
builder.append(address[pos++] & 0xFF).append('.');
builder.append(address[pos++] & 0xFF).append('.');
builder.append(address[pos++] & 0xFF);
if (address.length >= 6) {
builder.append(':').append(port);
}
}
/**
* 分解IP,只支持IPV4
*
* @param ip ip地址
* @return 分段
*/
public static int[] parseIp(final String ip) {
if (!isValidIpV4Address(ip)) {
return null;
}
if (ip == null || ip.isEmpty()) {
return null;
}
int[] parts = new int[4];
int index = 0;
int start = -1;
int end = -1;
int part;
char[] chars = ip.toCharArray();
char ch = 0;
for (int i = 0; i < chars.length; i++) {
if (index > 3) {
// 超过了4个数字
return null;
}
ch = chars[i];
switch (ch) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (start == -1) {
start = i;
}
end = i;
if (end - start > 2) {
// 超长了,最多3个数字
return null;
}
break;
case '.':
// 分隔符
if (start == -1) {
// 前面必须有字符
return null;
}
part = Integer.parseInt(new String(chars, start, end - start + 1));
if (part > 255) {
return null;
}
parts[index++] = part;
start = -1;
end = -1;
break;
default:
return null;
}
}
if (start > -1) {
part = Integer.parseInt(new String(chars, start, end - start + 1));
if (part > 255) {
return null;
}
parts[index] = part;
return index == 3 ? parts : null;
} else {
// 以.结尾
return null;
}
}
/**
* 把IP地址转换成长整型,只支持IPv4
*
* @param ip IP地址
* @return 长整形
*/
public static long toLong(final String ip) {
int[] data = parseIp(ip);
if (data == null) {
throw new IllegalArgumentException(String.format("invalid ip %s", ip));
}
long result = 0;
result += ((long) data[0]) << 24;
result += ((long) (data[1]) << 16);
result += ((long) (data[2]) << 8);
result += ((long) (data[3]));
return result;
}
/**
* 把长整形转换成IP地址,只支持IPv4
*
* @param ip 长整型
* @return IP字符串
*/
public static String toIp(long ip) {
StringBuffer sb = new StringBuffer(20);
long part1 = (ip & 0xFFFFFFFF) >>> 24;
long part2 = (ip & 0x00FFFFFF) >>> 16;
long part3 = (ip & 0x0000FFFF) >>> 8;
long part4 = ip & 0x000000FF;
//直接右移24位
sb.append(part1).append('.');
//将高8位置0,然后右移16位
sb.append(part2).append('.');
//将高16位置0,然后右移8位
sb.append(part3).append('.');
//将高24位置0
sb.append(part4);
return sb.toString();
}
public static void main(String[] args) throws Exception {
System.setProperty("java.net.preferIPv6Stack", "true");
List ips = getLocalIps();
ips.forEach(ip -> System.out.println(ip));
String ip = getLocalIp();
System.out.println("local ip :" + ip);
String v6Str = "fe80:0:0:0:10af:7794:db80:1ba1";
String v4Str = "127.0.0.1";
System.out.println(IPAddressUtil.isIPv4LiteralAddress(v6Str));
System.out.println(IPAddressUtil.isIPv6LiteralAddress(v4Str));
byte[] v4Byte = IPAddressUtil.textToNumericFormatV4(v4Str);
byte[] v6Byte = IPAddressUtil.textToNumericFormatV6(v6Str);
StringBuilder buffer = new StringBuilder();
IpUtil.toAddress(v4Byte, buffer);
System.out.println("buffer_v4:" + buffer.toString());
buffer = new StringBuilder();
IpUtil.toAddress(v6Byte, buffer);
System.out.println("hex_v6:" + Integer.toHexString(v6Byte[0] & 0xFF).toUpperCase());
System.out.println("hex_v6:" + Integer.toHexString(v6Byte[1] & 0xFF).toUpperCase());
System.out.println("buffer_v6:" + buffer.toString());
InetSocketAddress address = new InetSocketAddress(v6Str, 80);
v6Byte = address.getAddress().getAddress();
System.out.println("hex_v6_1:" + Integer.toHexString(v6Byte[0] & 0xFF).toUpperCase());
}
/**
* Creates an byte[] based on an ipAddressString. No error handling is
* performed here.
*/
private static byte[] toByteWithoutPort(String ipAddressString) {
if (isValidIpV4Address(ipAddressString)) {
StringTokenizer tokenizer = new StringTokenizer(ipAddressString, ".");
String token;
int tempInt;
byte[] byteAddress = new byte[4];
for (int i = 0; i < 4; i++) {
token = tokenizer.nextToken();
tempInt = Integer.parseInt(token);
byteAddress[i] = (byte) tempInt;
}
return byteAddress;
}
if (isValidIpV6Address(ipAddressString)) {
if (ipAddressString.charAt(0) == '[') {
ipAddressString = ipAddressString.substring(1, ipAddressString.length() - 1);
}
int percentPos = ipAddressString.indexOf('%');
if (percentPos >= 0) {
ipAddressString = ipAddressString.substring(0, percentPos);
}
StringTokenizer tokenizer = new StringTokenizer(ipAddressString, ":.", true);
ArrayList hexStrings = new ArrayList();
ArrayList decStrings = new ArrayList();
String token = "";
String prevToken = "";
int doubleColonIndex = -1; // If a double colon exists, we need to
// insert 0s.
// Go through the tokens, including the seperators ':' and '.'
// When we hit a : or . the previous token will be added to either
// the hex list or decimal list. In the case where we hit a ::
// we will save the index of the hexStrings so we can add zeros
// in to fill out the string
while (tokenizer.hasMoreTokens()) {
prevToken = token;
token = tokenizer.nextToken();
if (":".equals(token)) {
if (":".equals(prevToken)) {
doubleColonIndex = hexStrings.size();
} else if (!prevToken.isEmpty()) {
hexStrings.add(prevToken);
}
} else if (".".equals(token)) {
decStrings.add(prevToken);
}
}
if (":".equals(prevToken)) {
if (":".equals(token)) {
doubleColonIndex = hexStrings.size();
} else {
hexStrings.add(token);
}
} else if (".".equals(prevToken)) {
decStrings.add(token);
}
// figure out how many hexStrings we should have
// also check if it is a IPv4 address
int hexStringsLength = 8;
// If we have an IPv4 address tagged on at the end, subtract
// 4 bytes, or 2 hex words from the total
if (!decStrings.isEmpty()) {
hexStringsLength -= 2;
}
// if we hit a double Colon add the appropriate hex strings
if (doubleColonIndex != -1) {
int numberToInsert = hexStringsLength - hexStrings.size();
for (int i = 0; i < numberToInsert; i++) {
hexStrings.add(doubleColonIndex, "0");
}
}
byte[] ipByteArray = new byte[16];
// Finally convert these strings to bytes...
for (int i = 0; i < hexStrings.size(); i++) {
convertToBytes(hexStrings.get(i), ipByteArray, i * 2);
}
// Now if there are any decimal values, we know where they go...
for (int i = 0; i < decStrings.size(); i++) {
ipByteArray[i + 12] = (byte) (Integer.parseInt(decStrings.get(i)) & 255);
}
return ipByteArray;
}
return null;
}
/**
* Converts a 4 character hex word into a 2 byte word equivalent
*/
private static void convertToBytes(String hexWord, byte[] ipByteArray, int byteIndex) {
int hexWordLength = hexWord.length();
int hexWordIndex = 0;
ipByteArray[byteIndex] = 0;
ipByteArray[byteIndex + 1] = 0;
int charValue;
// high order 4 bits of first byte
if (hexWordLength > 3) {
charValue = getIntValue(hexWord.charAt(hexWordIndex++));
ipByteArray[byteIndex] |= charValue << 4;
}
// low order 4 bits of the first byte
if (hexWordLength > 2) {
charValue = getIntValue(hexWord.charAt(hexWordIndex++));
ipByteArray[byteIndex] |= charValue;
}
// high order 4 bits of second byte
if (hexWordLength > 1) {
charValue = getIntValue(hexWord.charAt(hexWordIndex++));
ipByteArray[byteIndex + 1] |= charValue << 4;
}
// low order 4 bits of the first byte
charValue = getIntValue(hexWord.charAt(hexWordIndex));
ipByteArray[byteIndex + 1] |= charValue & 15;
}
static int getIntValue(char c) {
switch (c) {
case '0':
return 0;
case '1':
return 1;
case '2':
return 2;
case '3':
return 3;
case '4':
return 4;
case '5':
return 5;
case '6':
return 6;
case '7':
return 7;
case '8':
return 8;
case '9':
return 9;
}
c = Character.toLowerCase(c);
switch (c) {
case 'a':
return 10;
case 'b':
return 11;
case 'c':
return 12;
case 'd':
return 13;
case 'e':
return 14;
case 'f':
return 15;
}
return 0;
}
public static boolean isValidIpV6Address(String ipAddress) {
int length = ipAddress.length();
boolean doubleColon = false;
int numberOfColons = 0;
int numberOfPeriods = 0;
int numberOfPercent = 0;
StringBuilder word = new StringBuilder();
char c = 0;
char prevChar;
int offset = 0; // offset for [] ip addresses
if (length < 2) {
return false;
}
for (int i = 0; i < length; i++) {
prevChar = c;
c = ipAddress.charAt(i);
switch (c) {
// case for an open bracket [x:x:x:...x]
case '[':
if (i != 0) {
return false; // must be first character
}
if (ipAddress.charAt(length - 1) != ']') {
return false; // must have a close ]
}
offset = 1;
if (length < 4) {
return false;
}
break;
// case for a closed bracket at end of IP [x:x:x:...x]
case ']':
if (i != length - 1) {
return false; // must be last charcter
}
if (ipAddress.charAt(0) != '[') {
return false; // must have a open [
}
break;
// case for the last 32-bits represented as IPv4 x:x:x:x:x:x:d.d.d.d
case '.':
numberOfPeriods++;
if (numberOfPeriods > 3) {
return false;
}
if (!isValidIp4Word(word.toString())) {
return false;
}
if (numberOfColons != 6 && !doubleColon) {
return false;
}
// a special case ::1:2:3:4:5:d.d.d.d allows 7 colons with an
// IPv4 ending, otherwise 7 :'s is bad
if (numberOfColons == 7 && ipAddress.charAt(offset) != ':' &&
ipAddress.charAt(1 + offset) != ':') {
return false;
}
word.delete(0, word.length());
break;
case ':':
// FIX "IP6 mechanism syntax #ip6-bad1"
// An IPV6 address cannot start with a single ":".
// Either it can starti with "::" or with a number.
if (i == offset && (ipAddress.length() <= i || ipAddress.charAt(i + 1) != ':')) {
return false;
}
// END FIX "IP6 mechanism syntax #ip6-bad1"
numberOfColons++;
if (numberOfColons > 7) {
return false;
}
if (numberOfPeriods > 0) {
return false;
}
if (prevChar == ':') {
if (doubleColon) {
return false;
}
doubleColon = true;
}
word.delete(0, word.length());
break;
case '%':
if (numberOfColons == 0) {
return false;
}
numberOfPercent++;
// validate that the stuff after the % is valid
if (i + 1 >= length) {
// in this case the percent is there but no number is
// available
return false;
}
try {
if (Integer.parseInt(ipAddress.substring(i + 1)) < 0) {
return false;
}
} catch (NumberFormatException e) {
// right now we just support an integer after the % so if
// this is not
// what is there then return
return false;
}
break;
default:
if (numberOfPercent == 0) {
if (word != null && word.length() > 3) {
return false;
}
if (!isValidHexChar(c)) {
return false;
}
}
word.append(c);
}
}
// Check if we have an IPv4 ending
if (numberOfPeriods > 0) {
// There is a test case with 7 colons and valid ipv4 this should resolve it
if (numberOfPeriods != 3 || !(isValidIp4Word(word.toString()) && numberOfColons < 7)) {
return false;
}
} else {
// If we're at then end and we haven't had 7 colons then there is a
// problem unless we encountered a doubleColon
if (numberOfColons != 7 && !doubleColon) {
return false;
}
// If we have an empty word at the end, it means we ended in either
// a : or a .
// If we did not end in :: then this is invalid
if (numberOfPercent == 0) {
if (word.length() == 0 && ipAddress.charAt(length - 1 - offset) == ':' &&
ipAddress.charAt(length - 2 - offset) != ':') {
return false;
}
}
}
return true;
}
public static boolean isValidIp4Word(String word) {
char c;
if (word.length() < 1 || word.length() > 3) {
return false;
}
for (int i = 0; i < word.length(); i++) {
c = word.charAt(i);
if (!(c >= '0' && c <= '9')) {
return false;
}
}
return Integer.parseInt(word) <= 255;
}
static boolean isValidHexChar(char c) {
return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f';
}
/**
* Takes a string and parses it to see if it is a valid IPV4 address.
*
* @return true, if the string represents an IPV4 address in dotted
* notation, false otherwise
*/
public static boolean isValidIpV4Address(String value) {
int periods = 0;
int i;
int length = value.length();
if (length > 15) {
return false;
}
char c;
StringBuilder word = new StringBuilder();
for (i = 0; i < length; i++) {
c = value.charAt(i);
if (c == '.') {
periods++;
if (periods > 3) {
return false;
}
if (word.length() == 0) {
return false;
}
if (Integer.parseInt(word.toString()) > 255) {
return false;
}
word.delete(0, word.length());
} else if (!Character.isDigit(c)) {
return false;
} else {
if (word.length() > 2) {
return false;
}
word.append(c);
}
}
if (word.length() == 0 || Integer.parseInt(word.toString()) > 255) {
return false;
}
return periods == 3;
}
}