001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.openstreetmap.josm.data.validation.routines; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.List; 022 023/** 024 * <p><b>InetAddress</b> validation and conversion routines (<code>java.net.InetAddress</code>).</p> 025 * 026 * <p>This class provides methods to validate a candidate IP address. 027 * 028 * <p> 029 * This class is a Singleton; you can retrieve the instance via the {@link #getInstance()} method. 030 * </p> 031 * 032 * @version $Revision: 1715439 $ 033 * @since Validator 1.4 034 */ 035public class InetAddressValidator extends AbstractValidator { 036 037 private static final int IPV4_MAX_OCTET_VALUE = 255; 038 039 private static final int MAX_UNSIGNED_SHORT = 0xffff; 040 041 private static final int BASE_16 = 16; 042 043 private static final String IPV4_REGEX = 044 "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"; 045 046 // Max number of hex groups (separated by :) in an IPV6 address 047 private static final int IPV6_MAX_HEX_GROUPS = 8; 048 049 // Max hex digits in each IPv6 group 050 private static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4; 051 052 /** 053 * Singleton instance of this class. 054 */ 055 private static final InetAddressValidator VALIDATOR = new InetAddressValidator(); 056 057 /** IPv4 RegexValidator */ 058 private final RegexValidator ipv4Validator = new RegexValidator(IPV4_REGEX); 059 060 /** 061 * Returns the singleton instance of this validator. 062 * @return the singleton instance of this validator 063 */ 064 public static InetAddressValidator getInstance() { 065 return VALIDATOR; 066 } 067 068 /** 069 * Checks if the specified string is a valid IP address. 070 * @param inetAddress the string to validate 071 * @return true if the string validates as an IP address 072 */ 073 @Override 074 public boolean isValid(String inetAddress) { 075 return isValidInet4Address(inetAddress) || isValidInet6Address(inetAddress); 076 } 077 078 @Override 079 public String getValidatorName() { 080 return null; 081 } 082 083 /** 084 * Validates an IPv4 address. Returns true if valid. 085 * @param inet4Address the IPv4 address to validate 086 * @return true if the argument contains a valid IPv4 address 087 */ 088 public boolean isValidInet4Address(String inet4Address) { 089 // verify that address conforms to generic IPv4 format 090 String[] groups = ipv4Validator.match(inet4Address); 091 092 if (groups == null) { 093 return false; 094 } 095 096 // verify that address subgroups are legal 097 for (String ipSegment : groups) { 098 if (ipSegment == null || ipSegment.isEmpty()) { 099 return false; 100 } 101 102 int iIpSegment = 0; 103 104 try { 105 iIpSegment = Integer.parseInt(ipSegment); 106 } catch (NumberFormatException e) { 107 return false; 108 } 109 110 if (iIpSegment > IPV4_MAX_OCTET_VALUE) { 111 return false; 112 } 113 114 if (ipSegment.length() > 1 && ipSegment.startsWith("0")) { 115 return false; 116 } 117 118 } 119 120 return true; 121 } 122 123 /** 124 * Validates an IPv6 address. Returns true if valid. 125 * @param inet6Address the IPv6 address to validate 126 * @return true if the argument contains a valid IPv6 address 127 * 128 * @since 1.4.1 129 */ 130 public boolean isValidInet6Address(String inet6Address) { 131 boolean containsCompressedZeroes = inet6Address.contains("::"); 132 if (containsCompressedZeroes && (inet6Address.indexOf("::") != inet6Address.lastIndexOf("::"))) { 133 return false; 134 } 135 if ((inet6Address.startsWith(":") && !inet6Address.startsWith("::")) 136 || (inet6Address.endsWith(":") && !inet6Address.endsWith("::"))) { 137 return false; 138 } 139 String[] octets = inet6Address.split(":"); 140 if (containsCompressedZeroes) { 141 List<String> octetList = new ArrayList<>(Arrays.asList(octets)); 142 if (inet6Address.endsWith("::")) { 143 // String.split() drops ending empty segments 144 octetList.add(""); 145 } else if (inet6Address.startsWith("::") && !octetList.isEmpty()) { 146 octetList.remove(0); 147 } 148 octets = octetList.toArray(new String[octetList.size()]); 149 } 150 if (octets.length > IPV6_MAX_HEX_GROUPS) { 151 return false; 152 } 153 int validOctets = 0; 154 int emptyOctets = 0; 155 for (int index = 0; index < octets.length; index++) { 156 String octet = octets[index]; 157 if (octet.length() == 0) { 158 emptyOctets++; 159 if (emptyOctets > 1) { 160 return false; 161 } 162 } else { 163 emptyOctets = 0; 164 if (octet.contains(".")) { // contains is Java 1.5+ 165 if (!inet6Address.endsWith(octet)) { 166 return false; 167 } 168 if (index > octets.length - 1 || index > 6) { // TODO magic number (sort of) 169 // IPV4 occupies last two octets 170 return false; 171 } 172 if (!isValidInet4Address(octet)) { 173 return false; 174 } 175 validOctets += 2; 176 continue; 177 } 178 if (octet.length() > IPV6_MAX_HEX_DIGITS_PER_GROUP) { 179 return false; 180 } 181 int octetInt = 0; 182 try { 183 octetInt = Integer.valueOf(octet, BASE_16).intValue(); 184 } catch (NumberFormatException e) { 185 return false; 186 } 187 if (octetInt < 0 || octetInt > MAX_UNSIGNED_SHORT) { 188 return false; 189 } 190 } 191 validOctets++; 192 } 193 if (validOctets < IPV6_MAX_HEX_GROUPS && !containsCompressedZeroes) { 194 return false; 195 } 196 return true; 197 } 198}