IpAddress.php 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  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. $binary = \inet_pton($ip);
  67. for ($i = 128; $i > $maskBits; $i -= 8) {
  68. $j = \intdiv($i, 8) - 1;
  69. $k = (int) \min(8, $i - $maskBits);
  70. $mask = (0xff - ((2 ** $k) - 1));
  71. $int = \unpack('C', $binary[$j]);
  72. $binary[$j] = \pack('C', $int[1] & $mask);
  73. }
  74. return \inet_ntop($binary).'/'.$maskBits;
  75. }
  76. /**
  77. * Gets either the /32 (IPv4) or the /128 (IPv6) subnet of an IP address
  78. *
  79. * @return string
  80. */
  81. public function getSubnet(): string {
  82. if (\preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $this->ip)) {
  83. return $this->getIPv4Subnet(
  84. $this->ip,
  85. 32
  86. );
  87. }
  88. return $this->getIPv6Subnet(
  89. $this->ip,
  90. 128
  91. );
  92. }
  93. /**
  94. * Returns the specified IP address
  95. *
  96. * @return string
  97. */
  98. public function __toString(): string {
  99. return $this->ip;
  100. }
  101. }