IPUtility.java
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2012-2014 Monty Program Ab
// Copyright (c) 2015-2025 MariaDB Corporation Ab
package org.mariadb.jdbc.util;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IPUtility {
/*
Check that host is an IP address or not, trying to avoid DNS resolution
*/
public static boolean isInetAddress(String ipString) {
if (ipString == null || ipString.isEmpty()) {
return false;
}
// Reject anything that could trigger hostname resolution.
// Allow only characters that can appear in numeric IP literals (+ optional IPv6 scope).
// Note: we intentionally do not validate scope IDs against local interfaces.
for (int i = 0; i < ipString.length(); i++) {
char c = ipString.charAt(i);
boolean ok =
(c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'f')
|| (c >= 'A' && c <= 'F')
|| c == '.'
|| c == ':'
|| c == '%';
if (!ok) {
return false;
}
}
int percent = ipString.indexOf('%');
String literal = (percent == -1) ? ipString : ipString.substring(0, percent);
if (literal.isEmpty()) {
return false;
}
// IPv4 (no scope allowed).
if (literal.indexOf(':') == -1) {
if (percent != -1) {
return false;
}
String[] parts = literal.split("\\.", -1);
if (parts.length != 4) {
return false;
}
for (String part : parts) {
if (part.isEmpty() || part.length() > 3) {
return false;
}
// Disallow leading zeros ("01") to match existing strict parsing behavior.
if (part.length() > 1 && part.charAt(0) == '0') {
return false;
}
int value = 0;
for (int i = 0; i < part.length(); i++) {
char c = part.charAt(i);
if (c < '0' || c > '9') {
return false;
}
value = value * 10 + (c - '0');
}
if (value > 255) {
return false;
}
}
return true;
}
// IPv6 (optional scope allowed). Delegate numeric parsing to the JDK without DNS.
// With the character filter above, this cannot be a hostname.
try {
return InetAddress.getByName(literal) instanceof Inet6Address;
} catch (UnknownHostException e) {
return false;
}
}
}