1 /*
2 * Copyright (C) 2020-2023 Dipl.-Inform. Kai Hofmann. All rights reserved!
3 */
4 package de.powerstat.validation.values;
5
6
7 import java.util.Objects;
8 import java.util.regex.Pattern;
9
10 import de.powerstat.validation.interfaces.IValueObject;
11
12
13 /**
14 * IP V4 address.
15 *
16 * DSGVO relevant.
17 *
18 * TODO convert to IP V6 format
19 * TODO https://datahub.io/core/geoip2-ipv4/r/geoip2-ipv4.csv
20 * TODO ping ok?
21 */
22 public final class IPV4Address implements Comparable<IPV4Address>, IValueObject
23 {
24 /* *
25 * Cache for singletons.
26 */
27 // private static final Map<String, IPV4Address> CACHE = new WeakHashMap<>();
28
29 /**
30 * Class c 192.
31 */
32 private static final String CLASS_C_192 = "192"; //$NON-NLS-1$
33
34 /**
35 * 100.
36 */
37 private static final String C100 = "100"; //$NON-NLS-1$
38
39 /**
40 * 198.
41 */
42 private static final String C198 = "198"; //$NON-NLS-1$
43
44 /**
45 * 0.
46 */
47 private static final String ZERO = "0"; //$NON-NLS-1$
48
49 /**
50 * IP V4 regexp.
51 */
52 private static final Pattern IPV4_REGEXP = Pattern.compile("^((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)$"); //$NON-NLS-1$
53
54 /**
55 * IP V4 address.
56 */
57 private final String address;
58
59 /**
60 * IP V4 address parts.
61 */
62 private final String[] parts;
63
64
65 /**
66 * Constructor.
67 *
68 * @param address IP V4 address
69 * @throws NullPointerException if address is null
70 * @throws IllegalArgumentException if address is not an ip v4 address
71 */
72 private IPV4Address(final String address)
73 {
74 super();
75 Objects.requireNonNull(address, "address"); //$NON-NLS-1$
76 if ((address.length() < 7) || (address.length() > 15))
77 {
78 throw new IllegalArgumentException("To short or long for an IP V4 address"); //$NON-NLS-1$
79 }
80 if (!IPV4Address.IPV4_REGEXP.matcher(address).matches())
81 {
82 throw new IllegalArgumentException("Not an IP V4 address"); //$NON-NLS-1$
83 }
84 this.address = address;
85 this.parts = address.split("\\."); //$NON-NLS-1$
86 }
87
88
89 /**
90 * IPV4Address factory.
91 *
92 * @param address IP V4 address
93 * @return IPV4Address object
94 */
95 public static IPV4Address of(final String address)
96 {
97 /*
98 synchronized (IPV4Address.class)
99 {
100 IPV4Address obj = IPV4Address.CACHE.get(address);
101 if (obj != null)
102 {
103 return obj;
104 }
105 obj = new IPV4Address(address);
106 IPV4Address.CACHE.put(address, obj);
107 return obj;
108 }
109 */
110 return new IPV4Address(address);
111 }
112
113
114 /**
115 * Is an IP V4 private address.
116 *
117 * 10.0.0.0–10.255.255.255 private, 1 8-Bit-Net 10.0.0.0/8 RFC 1918
118 * 172.16.0.0–172.31.255.255 private, 16 16-Bit-Nets 172.16.0.0/12 RFC 1918
119 * 192.168.0.0–192.168.255.255 private, 256 24-Bit-Nets 192.168.0.0/16 RFC 1918
120 * 169.254.0.0–169.254.255.255 link local, 1 16-Bit-Net 169.254.0.0/16 RFC 3927
121 *
122 * @return true when private address, otherwise false
123 */
124 public boolean isPrivate()
125 {
126 if ("10".equals(this.parts[0]) || (IPV4Address.CLASS_C_192.equals(this.parts[0]) && "168".equals(this.parts[1])) || ("169".equals(this.parts[0]) && "254".equals(this.parts[1]))) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
127 {
128 return true;
129 }
130 if ("172".equals(this.parts[0])) //$NON-NLS-1$
131 {
132 final int block2 = Integer.parseInt(this.parts[1]);
133 if ((block2 >= 16) && (block2 <= 31))
134 {
135 return true;
136 }
137 }
138 return false;
139 }
140
141
142 /**
143 * Is an IP V4 special address.
144 *
145 * 0.0.0.0/8 Das vorliegende Netzwerk RFC 1122
146 * 127.0.0.0/8 Loopback RFC 1122
147 * 100.64.0.0/10 Shared Transition Space RFC 6598
148 * 192.0.0.0/24 IETF Protocol Assignments RFC 6890
149 * 192.0.2.0/24 Test-Netzwerke RFC 6890
150 * 192.88.99.0/24 IPv6 zu IPv4 Relay (Veraltet) RFC 7526
151 * 198.18.0.0/15 Netzwerk-Benchmark-Tests RFC 2544
152 * 198.51.100.0/24 Test-Netzwerke RFC 6890
153 * 203.0.113.0/24 Test-Netzwerke RFC 6890
154 * 224.0.0.0/4 Multicasts RFC 5771
155 * 240.0.0.0/4 Reserviert RFC 1700
156 * 255.255.255.255/32 Limited Broadcast RFC 919, RFC 922
157 *
158 * @return true when special address, otherwise false
159 */
160 public boolean isSpecial()
161 {
162 if (IPV4Address.ZERO.equals(this.parts[0]) ||
163 "127".equals(this.parts[0]) || //$NON-NLS-1$
164 (IPV4Address.CLASS_C_192.equals(this.parts[0]) && IPV4Address.ZERO.equals(this.parts[1]) && IPV4Address.ZERO.equals(this.parts[2])) ||
165 (IPV4Address.CLASS_C_192.equals(this.parts[0]) && IPV4Address.ZERO.equals(this.parts[1]) && "2".equals(this.parts[2])) || //$NON-NLS-1$
166 (IPV4Address.CLASS_C_192.equals(this.parts[0]) && "88".equals(this.parts[1]) && "99".equals(this.parts[2])) || //$NON-NLS-1$ //$NON-NLS-2$
167 (IPV4Address.C198.equals(this.parts[0]) && "51".equals(this.parts[1]) && IPV4Address.C100.equals(this.parts[2])) || //$NON-NLS-1$
168 ("203".equals(this.parts[0]) && IPV4Address.ZERO.equals(this.parts[1]) && "113".equals(this.parts[2])) //$NON-NLS-1$ //$NON-NLS-2$
169 )
170 {
171 return true;
172 }
173 if (IPV4Address.C100.equals(this.parts[0]))
174 {
175 final int block2 = Integer.parseInt(this.parts[1]);
176 if ((block2 >= 64) && (block2 <= 127))
177 {
178 return true;
179 }
180 }
181 if (IPV4Address.C198.equals(this.parts[0]))
182 {
183 final int block2 = Integer.parseInt(this.parts[1]);
184 if ((block2 >= 18) && (block2 <= 19))
185 {
186 return true;
187 }
188 }
189 final int block1 = Integer.parseInt(this.parts[0]);
190 return (block1 >= 224);
191 }
192
193
194 /**
195 * Is an IP V4 public address.
196 *
197 * @return true when public address, otherwise false
198 */
199 public boolean isPublic()
200 {
201 return !isPrivate() && !isSpecial();
202 }
203
204
205 /**
206 * Returns the value of this IPV4Address as a string.
207 *
208 * @return The text value represented by this object after conversion to type string.
209 */
210 @Override
211 public String stringValue()
212 {
213 return this.address;
214 }
215
216
217 /**
218 * Calculate hash code.
219 *
220 * @return Hash
221 * @see java.lang.Object#hashCode()
222 */
223 @Override
224 public int hashCode()
225 {
226 return this.address.hashCode();
227 }
228
229
230 /**
231 * Is equal with another object.
232 *
233 * @param obj Object
234 * @return true when equal, false otherwise
235 * @see java.lang.Object#equals(java.lang.Object)
236 */
237 @Override
238 public boolean equals(final Object obj)
239 {
240 if (this == obj)
241 {
242 return true;
243 }
244 if (!(obj instanceof IPV4Address))
245 {
246 return false;
247 }
248 final IPV4Address other = (IPV4Address)obj;
249 return this.address.equals(other.address);
250 }
251
252
253 /**
254 * Returns the string representation of this IPV4Address.
255 *
256 * The exact details of this representation are unspecified and subject to change, but the following may be regarded as typical:
257 *
258 * "IPV4Address[address=192.168.0.0]"
259 *
260 * @return String representation of this IPV4Address
261 * @see java.lang.Object#toString()
262 */
263 @Override
264 public String toString()
265 {
266 final var builder = new StringBuilder(21);
267 builder.append("IPV4Address[address=").append(this.address).append(']'); //$NON-NLS-1$
268 return builder.toString();
269 }
270
271
272 /**
273 * Compare with another object.
274 *
275 * @param obj Object to compare with
276 * @return 0: equal; 1: greater; -1: smaller
277 * @see java.lang.Comparable#compareTo(java.lang.Object)
278 */
279 @Override
280 public int compareTo(final IPV4Address obj)
281 {
282 Objects.requireNonNull(obj, "obj"); //$NON-NLS-1$
283 return this.address.compareTo(obj.address);
284 }
285
286 }