IpAddress.php 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
  5. *
  6. * @author Lukas Reschke <lukas@statuscode.ch>
  7. *
  8. * @license GNU AGPL version 3 or any later version
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Affero General Public License as
  12. * published by the Free Software Foundation, either version 3 of the
  13. * License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. *
  23. */
  24. namespace OC\Security\Normalizer;
  25. /**
  26. * Class IpAddress is used for normalizing IPv4 and IPv6 addresses in security
  27. * relevant contexts in Nextcloud.
  28. *
  29. * @package OC\Security\Normalizer
  30. */
  31. class IpAddress {
  32. /** @var string */
  33. private $ip;
  34. /**
  35. * @param string $ip IP to normalized
  36. */
  37. public function __construct(string $ip) {
  38. $this->ip = $ip;
  39. }
  40. /**
  41. * Return the given subnet for an IPv4 address and mask bits
  42. *
  43. * @param string $ip
  44. * @param int $maskBits
  45. * @return string
  46. */
  47. private function getIPv4Subnet(string $ip, int $maskBits = 32): string {
  48. $binary = \inet_pton($ip);
  49. for ($i = 32; $i > $maskBits; $i -= 8) {
  50. $j = \intdiv($i, 8) - 1;
  51. $k = (int) \min(8, $i - $maskBits);
  52. $mask = (0xff - ((2 ** $k) - 1));
  53. $int = \unpack('C', $binary[$j]);
  54. $binary[$j] = \pack('C', $int[1] & $mask);
  55. }
  56. return \inet_ntop($binary).'/'.$maskBits;
  57. }
  58. /**
  59. * Return the given subnet for an IPv6 address and mask bits
  60. *
  61. * @param string $ip
  62. * @param int $maskBits
  63. * @return string
  64. */
  65. private function getIPv6Subnet(string $ip, int $maskBits = 48): string {
  66. if ($ip[0] === '[' && $ip[-1] === ']') { // If IP is with brackets, for example [::1]
  67. $ip = substr($ip, 1, strlen($ip) - 2);
  68. }
  69. $binary = \inet_pton($ip);
  70. for ($i = 128; $i > $maskBits; $i -= 8) {
  71. $j = \intdiv($i, 8) - 1;
  72. $k = (int) \min(8, $i - $maskBits);
  73. $mask = (0xff - ((2 ** $k) - 1));
  74. $int = \unpack('C', $binary[$j]);
  75. $binary[$j] = \pack('C', $int[1] & $mask);
  76. }
  77. return \inet_ntop($binary).'/'.$maskBits;
  78. }
  79. /**
  80. * Gets either the /32 (IPv4) or the /128 (IPv6) subnet of an IP address
  81. *
  82. * @return string
  83. */
  84. public function getSubnet(): string {
  85. if (\preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $this->ip)) {
  86. return $this->getIPv4Subnet(
  87. $this->ip,
  88. 32
  89. );
  90. }
  91. return $this->getIPv6Subnet(
  92. $this->ip,
  93. 128
  94. );
  95. }
  96. /**
  97. * Returns the specified IP address
  98. *
  99. * @return string
  100. */
  101. public function __toString(): string {
  102. return $this->ip;
  103. }
  104. }