/** * Copyright (c) 2012 SUSE LLC * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package com.redhat.rhn.common.validator; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Input validation for host[:port], where the host part can be either an IP address * (IPv4 or IPv6) or a hostname. */ public class HostPortValidator { // Singleton instance private static HostPortValidator instance; // Pattern to match IPv6 address in bracket notation private static final Pattern IPV6_BRACKETS = Pattern.compile("^\\[(.*)\\](:(\\d*))?$"); // Allow letters (of all languages), numbers, '.' and '-' private static final Pattern HOSTNAME = Pattern.compile("^[\\p{L}\\p{N}.-]*$"); // Private constructor private HostPortValidator() { } /** * Return the singleton instance of {@link HostPortValidator}. * @return {@link HostPortValidator} instance */ public static HostPortValidator getInstance() { if (instance == null) { instance = new HostPortValidator(); } return instance; } /** * Return true if the given string is a valid host[:port] representation. * @param hostPort the string with host[:port] * @return true if hostPort represents a valid host and port, else false. */ public boolean isValid(String hostPort) { if (hostPort == null || hostPort.isEmpty()) { return false; } String host; String port; if (hostPort.startsWith("[")) { // Parse an IPv6 address in bracket notation Matcher matcher = IPV6_BRACKETS.matcher(hostPort); if (!matcher.matches()) { return false; } host = matcher.group(1); port = matcher.group(3); } else { int colonIndex = hostPort.indexOf(':'); if (colonIndex != -1 && colonIndex == hostPort.lastIndexOf(':')) { // Split into host:port host = hostPort.substring(0, colonIndex); port = hostPort.substring(colonIndex + 1); } else { host = hostPort; port = null; } } // Validate host and port separately boolean isValidHost = true; // Validate IP addresses externally (v4 and v6) if (host.replaceAll("[\\d\\.]", "").isEmpty() || host.contains(":")) { isValidHost = isValidIP(host); } else { // Validate hostname charset Matcher matcher = HOSTNAME.matcher(host); isValidHost = matcher.matches() ? isValidHost : false; } boolean isValidPort = true; if (port != null) { isValidPort = isValidPort(port); } return isValidHost && isValidPort; } /** * Validate IP address format for a given string. * @param ipString * @return true if the given string is a valid IP, else false. */ private boolean isValidIP(String ipString) { boolean ret = false; if (ipString != null && !ipString.isEmpty()) { try { InetAddress.getByName(ipString); ret = true; } catch (UnknownHostException e) { // Stay silent } } return ret; } /** * Parse a port number from a given string and validate it. * @param portString * @return true if the given string is a valid port, else false. */ private boolean isValidPort(String portString) { boolean ret = false; if (portString != null && !portString.isEmpty()) { try { int port = Integer.parseInt(portString); ret = port >= 0 && port < 65535; } catch (NumberFormatException e) { // Stay silent } } return ret; } }