Add IPv6 support for isLocal checks (#1592)

* Add IPv6 support for isLocal checks

* Replace magic values like 127.0.0.1 and use our utility
* Support for IPv6 local adresses in IPv6 only or dual stack environments
    * Loopback [::1]
    * Site-Local fc00::/7
    * Link-local fe80::/10

* Introduce extra method for loopback addresses

* Use public IP for passMaxLogin check

* Use non-local IP addresses in test after change in verification
This commit is contained in:
games647
2018-07-04 02:05:17 +02:00
committed by Gabriele C
parent fc07ad3df1
commit 0227cb3f74
6 changed files with 104 additions and 30 deletions
@@ -18,6 +18,7 @@ import fr.xephi.authme.settings.commandconfig.CommandManager;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.InternetProtocolUtils;
import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.GameMode;
import org.bukkit.Server;
@@ -183,8 +184,7 @@ public class AsynchronousJoin implements AsynchronousProcess {
private boolean validatePlayerCountForIp(final Player player, String ip) {
if (service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP) > 0
&& !service.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)
&& !"127.0.0.1".equalsIgnoreCase(ip)
&& !"localhost".equalsIgnoreCase(ip)
&& !InternetProtocolUtils.isLoopbackAddress(ip)
&& countOnlinePlayersByIp(ip) > service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
@@ -30,6 +30,7 @@ import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.InternetProtocolUtils;
import fr.xephi.authme.util.PlayerUtils;
import fr.xephi.authme.util.Utils;
import org.bukkit.ChatColor;
@@ -325,8 +326,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
// Do not perform the check if player has multiple accounts permission or if IP is localhost
if (service.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP) <= 0
|| service.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)
|| "127.0.0.1".equalsIgnoreCase(ip)
|| "localhost".equalsIgnoreCase(ip)) {
|| InternetProtocolUtils.isLoopbackAddress(ip)) {
return false;
}
@@ -16,6 +16,7 @@ import fr.xephi.authme.service.bungeecord.BungeeSender;
import fr.xephi.authme.service.bungeecord.MessageType;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.InternetProtocolUtils;
import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.entity.Player;
@@ -119,8 +120,7 @@ public class AsyncRegister implements AsynchronousProcess {
final int maxRegPerIp = service.getProperty(RestrictionSettings.MAX_REGISTRATION_PER_IP);
final String ip = PlayerUtils.getPlayerIp(player);
if (maxRegPerIp > 0
&& !"127.0.0.1".equalsIgnoreCase(ip)
&& !"localhost".equalsIgnoreCase(ip)
&& !InternetProtocolUtils.isLoopbackAddress(ip)
&& !service.hasPermission(player, ALLOW_MULTIPLE_ACCOUNTS)) {
List<String> otherAccounts = database.getAllAuthsByIp(ip);
if (otherAccounts.size() >= maxRegPerIp) {
@@ -1,16 +1,13 @@
package fr.xephi.authme.util;
import java.util.regex.Pattern;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Utility class about the InternetProtocol
*/
public final class InternetProtocolUtils {
private static final Pattern LOCAL_ADDRESS_PATTERN =
Pattern.compile("(^127\\.)|(^(0)?10\\.)|(^172\\.(0)?1[6-9]\\.)|(^172\\.(0)?2[0-9]\\.)"
+ "|(^172\\.(0)?3[0-1]\\.)|(^169\\.254\\.)|(^192\\.168\\.)");
// Utility class
private InternetProtocolUtils() {
}
@@ -19,10 +16,57 @@ public final class InternetProtocolUtils {
* Checks if the specified address is a private or loopback address
*
* @param address address to check
*
* @return true if the address is a local or loopback address, false otherwise
* @return true if the address is a local (site and link) or loopback address, false otherwise
*/
public static boolean isLocalAddress(String address) {
return LOCAL_ADDRESS_PATTERN.matcher(address).find();
try {
InetAddress inetAddress = InetAddress.getByName(address);
// Examples: 127.0.0.1, localhost or [::1]
return isLoopbackAddress(address)
// Example: 10.0.0.0, 172.16.0.0, 192.168.0.0, fec0::/10 (deprecated)
// Ref: https://en.wikipedia.org/wiki/IP_address#Private_addresses
|| inetAddress.isSiteLocalAddress()
// Example: 169.254.0.0/16, fe80::/10
// Ref: https://en.wikipedia.org/wiki/IP_address#Address_autoconfiguration
|| inetAddress.isLinkLocalAddress()
// non deprecated unique site-local that java doesn't check yet -> fc00::/7
|| isIPv6UniqueSiteLocal(inetAddress);
} catch (UnknownHostException e) {
return false;
}
}
/**
* Checks if the specified address is a loopback address. This can be one of the following:
* <ul>
* <li>127.0.0.1</li>
* <li>localhost</li>
* <li>[::1]</li>
* </ul>
*
* @param address address to check
* @return true if the address is a loopback one
*/
public static boolean isLoopbackAddress(String address) {
try {
InetAddress inetAddress = InetAddress.getByName(address);
return inetAddress.isLoopbackAddress();
} catch (UnknownHostException e) {
return false;
}
}
private static boolean isLoopbackAddress(InetAddress address) {
return address.isLoopbackAddress();
}
private static boolean isIPv6UniqueSiteLocal(InetAddress address) {
// ref: https://en.wikipedia.org/wiki/Unique_local_address
// currently undefined but could be used in the near future fc00::/8
return (address.getAddress()[0] & 0xFF) == 0xFC
// in use for unique site-local fd00::/8
|| (address.getAddress()[0] & 0xFF) == 0xFD;
}
}