| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- <?php
- /*
- * Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
- * SPDX-License-Identifier: MIT
- */
- declare(strict_types=1);
- namespace Respect\Validation\Rules;
- use Respect\Validation\Exceptions\ComponentException;
- use function bccomp;
- use function explode;
- use function filter_var;
- use function ip2long;
- use function is_string;
- use function long2ip;
- use function mb_strpos;
- use function mb_substr_count;
- use function sprintf;
- use function str_repeat;
- use function str_replace;
- use function strtr;
- use const FILTER_VALIDATE_IP;
- /**
- * Validates whether the input is a valid IP address.
- *
- * This validator uses the native filter_var() PHP function.
- *
- * @author Alexandre Gomes Gaigalas <alganet@gmail.com>
- * @author Danilo Benevides <danilobenevides01@gmail.com>
- * @author Henrique Moody <henriquemoody@gmail.com>
- * @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
- */
- final class Ip extends AbstractRule
- {
- /**
- * @var string|null
- */
- private $range;
- /**
- * @var int|null
- */
- private $options;
- /**
- * @var string|null
- */
- private $startAddress;
- /**
- * @var string|null
- */
- private $endAddress;
- /**
- * @var string|null
- */
- private $mask;
- /**
- * Initializes the rule defining the range and some options for filter_var().
- *
- * @throws ComponentException In case the range is invalid
- */
- public function __construct(string $range = '*', ?int $options = null)
- {
- $this->parseRange($range);
- $this->range = $this->createRange();
- $this->options = $options;
- }
- /**
- * {@inheritDoc}
- */
- public function validate($input): bool
- {
- if (!is_string($input)) {
- return false;
- }
- if (!$this->verifyAddress($input)) {
- return false;
- }
- if ($this->mask) {
- return $this->belongsToSubnet($input);
- }
- if ($this->startAddress && $this->endAddress) {
- return $this->verifyNetwork($input);
- }
- return true;
- }
- private function createRange(): ?string
- {
- if ($this->startAddress && $this->endAddress) {
- return $this->startAddress . '-' . $this->endAddress;
- }
- if ($this->startAddress && $this->mask) {
- return $this->startAddress . '/' . long2ip((int) $this->mask);
- }
- return null;
- }
- private function parseRange(string $input): void
- {
- if ($input == '*' || $input == '*.*.*.*' || $input == '0.0.0.0-255.255.255.255') {
- return;
- }
- if (mb_strpos($input, '-') !== false) {
- [$this->startAddress, $this->endAddress] = explode('-', $input);
- if ($this->startAddress !== null && !$this->verifyAddress($this->startAddress)) {
- throw new ComponentException('Invalid network range');
- }
- if ($this->endAddress !== null && !$this->verifyAddress($this->endAddress)) {
- throw new ComponentException('Invalid network range');
- }
- return;
- }
- if (mb_strpos($input, '*') !== false) {
- $this->parseRangeUsingWildcards($input);
- return;
- }
- if (mb_strpos($input, '/') !== false) {
- $this->parseRangeUsingCidr($input);
- return;
- }
- throw new ComponentException('Invalid network range');
- }
- private function fillAddress(string $address, string $fill = '*'): string
- {
- return $address . str_repeat('.' . $fill, 3 - mb_substr_count($address, '.'));
- }
- private function parseRangeUsingWildcards(string $input): void
- {
- $address = $this->fillAddress($input);
- $this->startAddress = strtr($address, '*', '0');
- $this->endAddress = str_replace('*', '255', $address);
- }
- private function parseRangeUsingCidr(string $input): void
- {
- $parts = explode('/', $input);
- $this->startAddress = $this->fillAddress($parts[0], '0');
- $isAddressMask = mb_strpos($parts[1], '.') !== false;
- if ($isAddressMask && $this->verifyAddress($parts[1])) {
- $this->mask = sprintf('%032b', ip2long($parts[1]));
- return;
- }
- if ($isAddressMask || $parts[1] < 8 || $parts[1] > 30) {
- throw new ComponentException('Invalid network mask');
- }
- $this->mask = sprintf('%032b', ip2long((string) long2ip(~(2 ** (32 - (int) $parts[1]) - 1))));
- }
- private function verifyAddress(string $address): bool
- {
- return filter_var($address, FILTER_VALIDATE_IP, ['flags' => $this->options]) !== false;
- }
- private function verifyNetwork(string $input): bool
- {
- $input = sprintf('%u', ip2long($input));
- return $this->startAddress !== null
- && $this->endAddress !== null
- && bccomp($input, sprintf('%u', ip2long($this->startAddress))) >= 0
- && bccomp($input, sprintf('%u', ip2long($this->endAddress))) <= 0;
- }
- private function belongsToSubnet(string $input): bool
- {
- if ($this->mask === null || $this->startAddress === null) {
- return false;
- }
- $min = sprintf('%032b', ip2long($this->startAddress));
- $input = sprintf('%032b', ip2long($input));
- return ($input & $this->mask) === ($min & $this->mask);
- }
- }
|