View Javadoc
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  }