/*
* Copyright 2015-2025 the original author or 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 sockslib.common;
import com.google.common.hash.Hashing;
import sockslib.utils.UnsignedByte;
import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkArgument;
/**
* The class <code>IP</code> represents an IP v4 address.
*
* @author Youchao Feng
* @version 1.0
* @date May 2, 2015 12:50:28 AM
*/
public class IP implements Comparable<IP>, Serializable {
private static final long serialVersionUID = 1L;
/**
* Value of IP as long.
*/
private final long value;
/**
* First number of the IP address.
*/
private final int a;
/**
* Second number of the IP address.
*/
private final int b;
/**
* Third number of the IP address.
*/
private final int c;
/**
* Last number of the IP address.
*/
private final int d;
/**
* Constructs IP by four numbers.
*
* @param a First number of the IP address.
* @param b Second number of the IP address.
* @param c Third number of the IP address.
* @param d Last number of the IP address.
*/
public IP(int a, int b, int c, int d) {
checkArgument(checkRange(a) && checkRange(b) && checkRange(c)
&& checkRange(d), "Each number of IP must in 0 ~ 255");
this.a = a;
this.b = b;
this.c = c;
this.d = d;
value = this.toLong();
}
/**
* Constructs IP by a long integer.
*
* @param ip IP as Long integer.
*/
public IP(long ip) {
checkArgument(ip <= 0xffffffffL && ip >= 0, "Invalid IP");
value = ip;
a = (int) (ip >>> 24);
b = (int) ((ip & 0x00ffffff) >>> 16);
c = (int) ((ip & 0x0000ffff) >>> 8);
d = (int) (ip & 0x000000ff);
}
/**
* Constructs IP by bytes.
*
* @param address Bytes of address.
*/
public IP(byte[] address) {
a = UnsignedByte.toInt(address[0]);
b = UnsignedByte.toInt(address[1]);
c = UnsignedByte.toInt(address[2]);
d = UnsignedByte.toInt(address[3]);
value = this.toLong();
}
/**
* Creates a IP instance by a string.
*
* @param ip IP as a string. such as "192.168.1.1".
* @return Instance of <code>Ip</code>.
*/
public static IP parseFromString(String ip) {
String regex = "\\s*(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\s*";
Pattern pattern = Pattern.compile(regex);
Matcher m = pattern.matcher(ip);
checkArgument(m.find(), "IP string should match the regex:%s", regex);
int a = Integer.parseInt(m.group(1));
int b = Integer.parseInt(m.group(2));
int c = Integer.parseInt(m.group(3));
int d = Integer.parseInt(m.group(4));
return new IP(a, b, c, d);
}
/**
* Gets max IP which represents <b>255.255.255.255]</b>.
*
* @return Max IP address.
*/
public static IP MAX_IP() {
return new IP(0xffffffffL);
}
/**
* Gets minimum IP which represents <b>0.0.0.0</b>.
*
* @return Minimum IP address.
*/
public static IP MIN_IP() {
return new IP(0L);
}
public static boolean isValid(String ip) {
try {
IP.parseFromString(ip);
} catch (Exception e) {
return false;
}
return true;
}
public int getA() {
return a;
}
public int getB() {
return b;
}
public int getC() {
return c;
}
public int getD() {
return d;
}
/**
* Gets next IP address. If the IP address doesn't have next IP then return <code>null</code>.
*
* @return Next IP address.
*/
public IP nextIP() {
return new IP(value + 1);
}
/**
* Gets previous IP address. If the IP address doesn't have previous IP then return
* <code>null</code>.
*
* @return Previous IP address.
*/
public IP preIP() {
return new IP(value - 1);
}
/**
* Returns <code>true</code> if the IP is local IP address.
*
* @return <code>true</code> if the IP is local IP address.
*/
public boolean isLocalIP() {
return IPRange.AClassLocalIPRange().contains(this)
|| IPRange.BClassLocalIPRange().contains(this)
|| IPRange.CClassLocalIPRange().contains(this);
}
/**
* Returns <code>true</code> if the IP can be used in Internet.
*
* @return <code>true</code> if the IP can be used in Internet.
*/
public boolean isUseInInternet() {
return !isLocalIP();
}
/**
* Returns IP as a long integer.
*
* @return IP as a long integer.
*/
public long toLong() {
long a = this.a;
long b = this.b;
long c = this.c;
long d = this.d;
return ((a << 24) | (b << 16) | (c << 8) | d);
}
@Override
public int compareTo(IP ip) {
return (value > ip.getValue() ? 1 : value < ip.getValue() ? -1 : 0);
}
public long getValue() {
return value;
}
@Override
public String toString() {
return String.format("%s.%s.%s.%s", a, b, c, d);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof IP) {
IP ip = (IP) obj;
if (getValue() == ip.getValue()) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return Hashing.md5().newHasher().putInt(a).putChar('.').putInt(b).putChar('.').putInt(c)
.putChar('.').putInt(d).hash().hashCode();
}
/**
* Returns <code>true</code> if the number is in 0~255.
*
* @param num a number.
* @return <code>true</code> if the number is in 0~255.
*/
private boolean checkRange(int num) {
return num >= 0 && num <= 255;
}
}