Skip to content

Commit

Permalink
Add CIDR Ip validator
Browse files Browse the repository at this point in the history
Signed-off-by: daverner <daverner@sqli.com>
  • Loading branch information
daverner committed Jan 27, 2021
1 parent 7d91d44 commit 4f11faa
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 1 deletion.
16 changes: 15 additions & 1 deletion README_toolbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,18 @@ Save some actions made in backoffice into a log file

Clean name of the file uploaded in backoffice when moving into storage to prevent SEO penalties.
Change special characters to their latin correspondence when it's possible (else they will be removed), replace spaces and force lower case.


### IP CIDR Validator

You can check if an IP is in CIDR range with this :

```php
$ipConstraint = new \SQLI\EzToolboxBundle\Validator\Constraints\IpCidr(['cidr' => "192.168.0.0/24"]);
$errors = $this->validator->validate( '192.168.1.10', $ipConstraint );
if(count($errors)) {
$message = $errors[0]->getMessage();
}
```

In this case, $message contains `192.168.1.10 not validated with CIDR mask 192.168.0.0/24`
51 changes: 51 additions & 0 deletions Validator/Constraints/IpCidr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace SQLI\EzToolboxBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

class IpCidr extends Constraint
{
const INVALID_MASK = '9d459b65f2090feb1c9c89652e18d43a';
const INVALID_IP = '2828df84b112c4bb456ef64e2a5bfefb';
const NOT_IN_MASK = 'f96ade9a9e82b6253d680b39518f9f16';
protected static $errorNames = [
self::INVALID_MASK => 'INVALID_MASK',
self::INVALID_IP => 'INVALID_IP',
self::NOT_IN_MASK => 'NOT_IN_MASK',
];
public $message = "{{ value }} not validated with CIDR mask {{ cidr }}";
public $cidr;

/**
* {@inheritdoc}
*/
public function __construct($options = null)
{
parent::__construct($options);
}

/**
* Returns the name of the default option.
* Override this method to define a default option.
*
* @return string|null
* @see __construct()
*/
public function getDefaultOption()
{
return 'cidr';
}

/**
* Returns the name of the required options.
* Override this method if you want to define required options.
*
* @return array
* @see __construct()
*/
public function getRequiredOptions()
{
return ['cidr'];
}
}
73 changes: 73 additions & 0 deletions Validator/Constraints/IpCidrValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace SQLI\EzToolboxBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

class IpCidrValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof IpCidr) {
throw new UnexpectedTypeException($constraint, IpCidr::class);
}

if (null === $value || '' === $value) {
return;
}

if (!is_string($value)) {
throw new UnexpectedTypeException($value, 'string');
}

if (!$this->checkCidrMask($value, $constraint->cidr)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $value)
->setParameter('{{ cidr }}', $constraint->cidr)
->setCode(IpCidr::NOT_IN_MASK)
->setInvalidValue($value)
->addViolation();
}
}

/**
* Check if $iptocheck is in range defined by $CIDR
* Ex: 192.168.0.1 in 192.168.0.0/24 => true
* Ex: 192.168.1.1 in 192.168.0.0/24 => false
*
* @param string $iptocheck
* @param string $CIDR
* @return bool
*/
private function checkCidrMask($iptocheck, $CIDR): bool
{
/* get the base and the bits from the ban in the database */
list($base, $bits) = explode('/', $CIDR);

/* now split it up into it's classes */
list($a, $b, $c, $d) = explode('.', $base);

/* now do some bit shfiting/switching to convert to ints */
$i = ($a << 24) + ($b << 16) + ($c << 8) + $d;
$mask = $bits == 0 ? 0 : (~0 << (32 - $bits));

/* here's our lowest int */
$low = $i & $mask;

/* here's our highest int */
$high = $i | (~$mask & 0xFFFFFFFF);

/* now split the ip were checking against up into classes */
list($a, $b, $c, $d) = explode('.', $iptocheck);

/* now convert the ip we're checking against to an int */
$check = ($a << 24) + ($b << 16) + ($c << 8) + $d;

/* if the ip is within the range, including
highest/lowest values, then it's witin the CIDR range */

return ($check >= $low && $check <= $high);
}
}

0 comments on commit 4f11faa

Please # to comment.