Skip to content

Commit b2dd76d

Browse files
committed
IPAddressArgumentValueValidator improvements
Improved the IP address argument value validator to catch additional types of malformed IPv4 addresses that were previously accepted. Also, improved performance when validating IPv4 addresses.
1 parent d9549c6 commit b2dd76d

File tree

4 files changed

+211
-37
lines changed

4 files changed

+211
-37
lines changed

docs/release-notes.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ <h3>Version 7.0.3</h3>
7070
<br><br>
7171
</li>
7272

73+
<li>
74+
Improved the IP address argument value validator to catch additional types of
75+
malformed IPv4 addresses that were previously accepted. Also, improved
76+
performance when validating IPv4 addresses.
77+
<br><br>
78+
</li>
79+
7380
<li>
7481
Updated client-side support for the Ping Identity Directory Server's version
7582
monitor entry to handle attributes used to indicate whether the server is running

messages/unboundid-ldapsdk-args.properties

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,8 @@ ERR_TIMESTAMP_RANGE_VALIDATOR_TOO_NEW=The provided value ''{0}'' for \
418418
ERR_IP_VALIDATOR_ILLEGAL_IPV6_CHAR=Value ''{0}'' provided for argument \
419419
''{1}'' is not acceptable because it is suspected to be an IPv6 address \
420420
but contains ''{0}'' that is not allowed to appear in IPv6 addresses.
421-
ERR_IP_VALIDATOR_ILLEGAL_IPV4_CHAR=Value ''{0}'' provided for argument \
422-
''{1}'' is not acceptable because it is suspected to be an IPv4 address \
423-
but contains ''{0}'' that is not allowed to appear in IPv4 addresses.
421+
ERR_IP_VALIDATOR_INVALID_IPV4_ADDRESS=Value ''{0}'' provided for argument \
422+
''{1}'' is not acceptable because it appears to be a malformed IPv4 address.
424423
ERR_IP_VALIDATOR_MALFORMED=Value ''{0}'' provided for argument ''{1}'' is \
425424
not a valid IP address.
426425
ERR_IP_VALIDATOR_IPV4_NOT_ACCEPTED=Value ''{0}'' provided for argument \

src/com/unboundid/util/args/IPAddressArgumentValueValidator.java

Lines changed: 76 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -173,20 +173,31 @@ public void validateArgumentValue(@NotNull final Argument argument,
173173
valueString, argument.getIdentifierString(), c));
174174
}
175175
}
176+
177+
178+
// If we've gotten here, then we know that the value string contains only
179+
// characters that are allowed in an IPv6 address literal. Let
180+
// InetAddress.getByName do the heavy lifting for the rest of the
181+
// validation.
182+
try
183+
{
184+
LDAPConnectionOptions.DEFAULT_NAME_RESOLVER.getByName(valueString);
185+
}
186+
catch (final Exception e)
187+
{
188+
Debug.debugException(e);
189+
throw new ArgumentException(
190+
ERR_IP_VALIDATOR_MALFORMED.get(valueString,
191+
argument.getIdentifierString()),
192+
e);
193+
}
176194
}
177195
else if (valueString.indexOf('.') >= 0)
178196
{
179-
for (final char c : valueString.toCharArray())
197+
if (! isValidNumericIPv4Address(valueString))
180198
{
181-
if ((c == '.') || ((c >= '0') && (c <= '9')))
182-
{
183-
// This character is allowed in an IPv4 address.
184-
}
185-
else
186-
{
187-
throw new ArgumentException(ERR_IP_VALIDATOR_ILLEGAL_IPV4_CHAR.get(
188-
valueString, argument.getIdentifierString(), c));
189-
}
199+
throw new ArgumentException(ERR_IP_VALIDATOR_INVALID_IPV4_ADDRESS.get(
200+
valueString, argument.getIdentifierString()));
190201
}
191202
}
192203
else
@@ -196,24 +207,6 @@ else if (valueString.indexOf('.') >= 0)
196207
}
197208

198209

199-
// If we've gotten here, then we know that the value string contains only
200-
// characters that are allowed in IP address literal. Let
201-
// InetAddress.getByName do the heavy lifting for the rest of the
202-
// validation.
203-
try
204-
{
205-
LDAPConnectionOptions.DEFAULT_NAME_RESOLVER.getByName(valueString);
206-
}
207-
catch (final Exception e)
208-
{
209-
Debug.debugException(e);
210-
throw new ArgumentException(
211-
ERR_IP_VALIDATOR_MALFORMED.get(valueString,
212-
argument.getIdentifierString()),
213-
e);
214-
}
215-
216-
217210
if (isIPv6)
218211
{
219212
if (! acceptIPv6Addresses)
@@ -263,28 +256,77 @@ public static boolean isValidNumericIPv4Address(@Nullable final String s)
263256
return false;
264257
}
265258

259+
final StringBuilder octetBuffer = new StringBuilder();
260+
261+
int numPeriodsFound = 0;
266262
for (final char c : s.toCharArray())
267263
{
268-
if ((c == '.') || ((c >= '0') && (c <= '9')))
264+
if (c == '.')
269265
{
270-
// This character is allowed in an IPv4 address.
266+
if (! isValidNumericIPv4AddressOctet(octetBuffer.toString()))
267+
{
268+
return false;
269+
}
270+
271+
numPeriodsFound++;
272+
octetBuffer.setLength(0);
271273
}
272274
else
273275
{
274-
return false;
276+
octetBuffer.append(c);
275277
}
276278
}
277279

280+
if (numPeriodsFound != 3)
281+
{
282+
return false;
283+
}
284+
285+
if (! isValidNumericIPv4AddressOctet(octetBuffer.toString()))
286+
{
287+
return false;
288+
}
289+
290+
return true;
291+
}
292+
293+
294+
295+
/**
296+
* Indicates whether the provided string represents a valid octet within an
297+
* IPv4 address.
298+
*
299+
* @param s The string for which to make the determination.
300+
*
301+
* @return {@code true} if the provided string represents a valid IPv4
302+
* address octet, or {@code false} if not.
303+
*/
304+
private static boolean isValidNumericIPv4AddressOctet(@NotNull final String s)
305+
{
306+
if (s.isEmpty())
307+
{
308+
// The octet cannot be empty.
309+
return false;
310+
}
311+
278312
try
279313
{
280-
LDAPConnectionOptions.DEFAULT_NAME_RESOLVER.getByName(s);
281-
return true;
314+
final int octetValue = Integer.parseInt(s);
315+
if ((octetValue < 0) || (octetValue > 255))
316+
{
317+
// The octet value is out of range.
318+
return false;
319+
}
282320
}
283-
catch (final Exception e)
321+
catch (final NumberFormatException e)
284322
{
285323
Debug.debugException(e);
324+
325+
// The address has a non-numeric octet.
286326
return false;
287327
}
328+
329+
return true;
288330
}
289331

290332

tests/unit/src/com/unboundid/util/args/IPAddressArgumentValueValidatorTestCase.java

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,111 @@ public Object[][] getTestData()
392392
false
393393
},
394394

395+
new Object[]
396+
{
397+
"1.2.3",
398+
false,
399+
false
400+
},
401+
402+
new Object[]
403+
{
404+
"1.2.3.",
405+
false,
406+
false
407+
},
408+
409+
new Object[]
410+
{
411+
".1.2.3",
412+
false,
413+
false
414+
},
415+
416+
new Object[]
417+
{
418+
"1.2.x.4",
419+
false,
420+
false
421+
},
422+
423+
new Object[]
424+
{
425+
"1.2.3.x",
426+
false,
427+
false
428+
},
429+
430+
new Object[]
431+
{
432+
"1.2.3.4.",
433+
false,
434+
false
435+
},
436+
437+
new Object[]
438+
{
439+
"1.2.3.4.5",
440+
false,
441+
false
442+
},
443+
444+
new Object[]
445+
{
446+
"1234.5.6.7",
447+
false,
448+
false
449+
},
450+
451+
new Object[]
452+
{
453+
"1.2345.6.7",
454+
false,
455+
false
456+
},
457+
458+
new Object[]
459+
{
460+
"1.2.3456.7",
461+
false,
462+
false
463+
},
464+
465+
new Object[]
466+
{
467+
"1.2.3.4567",
468+
false,
469+
false
470+
},
471+
472+
new Object[]
473+
{
474+
"-1.2.3.4",
475+
false,
476+
false
477+
},
478+
479+
new Object[]
480+
{
481+
"1.-2.3.4",
482+
false,
483+
false
484+
},
485+
486+
new Object[]
487+
{
488+
"1.2.-3.4",
489+
false,
490+
false
491+
},
492+
493+
new Object[]
494+
{
495+
"1.2.3.-4",
496+
false,
497+
false
498+
},
499+
395500
new Object[]
396501
{
397502
"1..2..3..4",
@@ -406,6 +511,27 @@ public Object[][] getTestData()
406511
false
407512
},
408513

514+
new Object[]
515+
{
516+
"1..2.3.4",
517+
false,
518+
false
519+
},
520+
521+
new Object[]
522+
{
523+
"1.2..3.4",
524+
false,
525+
false
526+
},
527+
528+
new Object[]
529+
{
530+
"1.2.3..4",
531+
false,
532+
false
533+
},
534+
409535
new Object[]
410536
{
411537
"::",

0 commit comments

Comments
 (0)