1 | /*
|
---|
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
|
---|
3 | * contributor license agreements. See the NOTICE file distributed with
|
---|
4 | * this work for additional information regarding copyright ownership.
|
---|
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
|
---|
6 | * (the "License"); you may not use this file except in compliance with
|
---|
7 | * the License. You may obtain a copy of the License at
|
---|
8 | *
|
---|
9 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
10 | *
|
---|
11 | * Unless required by applicable law or agreed to in writing, software
|
---|
12 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
14 | * See the License for the specific language governing permissions and
|
---|
15 | * limitations under the License.
|
---|
16 | */
|
---|
17 | package org.openstreetmap.josm.data.validation.routines;
|
---|
18 |
|
---|
19 | import java.util.ArrayList;
|
---|
20 | import java.util.Arrays;
|
---|
21 | import java.util.List;
|
---|
22 |
|
---|
23 | /**
|
---|
24 | * <p><b>InetAddress</b> validation and conversion routines (<code>java.net.InetAddress</code>).</p>
|
---|
25 | *
|
---|
26 | * <p>This class provides methods to validate a candidate IP address.
|
---|
27 | *
|
---|
28 | * <p>
|
---|
29 | * This class is a Singleton; you can retrieve the instance via the {@link #getInstance()} method.
|
---|
30 | * </p>
|
---|
31 | *
|
---|
32 | * @version $Revision: 1741724 $
|
---|
33 | * @since Validator 1.4
|
---|
34 | */
|
---|
35 | public class InetAddressValidator extends AbstractValidator {
|
---|
36 |
|
---|
37 | private static final int IPV4_MAX_OCTET_VALUE = 255;
|
---|
38 |
|
---|
39 | private static final int MAX_UNSIGNED_SHORT = 0xffff;
|
---|
40 |
|
---|
41 | private static final int BASE_16 = 16;
|
---|
42 |
|
---|
43 | private static final String IPV4_REGEX =
|
---|
44 | "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$";
|
---|
45 |
|
---|
46 | // Max number of hex groups (separated by :) in an IPV6 address
|
---|
47 | private static final int IPV6_MAX_HEX_GROUPS = 8;
|
---|
48 |
|
---|
49 | // Max hex digits in each IPv6 group
|
---|
50 | private static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4;
|
---|
51 |
|
---|
52 | /**
|
---|
53 | * Singleton instance of this class.
|
---|
54 | */
|
---|
55 | private static final InetAddressValidator VALIDATOR = new InetAddressValidator();
|
---|
56 |
|
---|
57 | /** IPv4 RegexValidator */
|
---|
58 | private final RegexValidator ipv4Validator = new RegexValidator(IPV4_REGEX);
|
---|
59 |
|
---|
60 | /**
|
---|
61 | * Returns the singleton instance of this validator.
|
---|
62 | * @return the singleton instance of this validator
|
---|
63 | */
|
---|
64 | public static InetAddressValidator getInstance() {
|
---|
65 | return VALIDATOR;
|
---|
66 | }
|
---|
67 |
|
---|
68 | /**
|
---|
69 | * Checks if the specified string is a valid IP address.
|
---|
70 | * @param inetAddress the string to validate
|
---|
71 | * @return true if the string validates as an IP address
|
---|
72 | */
|
---|
73 | @Override
|
---|
74 | public boolean isValid(String inetAddress) {
|
---|
75 | return isValidInet4Address(inetAddress) || isValidInet6Address(inetAddress);
|
---|
76 | }
|
---|
77 |
|
---|
78 | @Override
|
---|
79 | public String getValidatorName() {
|
---|
80 | return null;
|
---|
81 | }
|
---|
82 |
|
---|
83 | /**
|
---|
84 | * Validates an IPv4 address. Returns true if valid.
|
---|
85 | * @param inet4Address the IPv4 address to validate
|
---|
86 | * @return true if the argument contains a valid IPv4 address
|
---|
87 | */
|
---|
88 | public boolean isValidInet4Address(String inet4Address) {
|
---|
89 | // verify that address conforms to generic IPv4 format
|
---|
90 | String[] groups = ipv4Validator.match(inet4Address);
|
---|
91 |
|
---|
92 | if (groups == null) {
|
---|
93 | return false;
|
---|
94 | }
|
---|
95 |
|
---|
96 | // verify that address subgroups are legal
|
---|
97 | for (String ipSegment : groups) {
|
---|
98 | if (ipSegment == null || ipSegment.isEmpty()) {
|
---|
99 | 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) { // CHECKSTYLE IGNORE MagicNumber
|
---|
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.parseInt(octet, BASE_16);
|
---|
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 | return validOctets >= IPV6_MAX_HEX_GROUPS || containsCompressedZeroes;
|
---|
194 | }
|
---|
195 | }
|
---|