Ridding of IPAddress and IPAddressRange, the latter was broken and the former was obsoleted by INET funcs.
This commit is contained in:
parent
b0f6db2450
commit
012171635e
11 changed files with 67 additions and 409 deletions
|
@ -30,6 +30,7 @@ require_once __DIR__ . '/src/colour.php';
|
|||
require_once __DIR__ . '/src/comments.php';
|
||||
require_once __DIR__ . '/src/general.php';
|
||||
require_once __DIR__ . '/src/git.php';
|
||||
require_once __DIR__ . '/src/ip.php';
|
||||
require_once __DIR__ . '/src/manage.php';
|
||||
require_once __DIR__ . '/src/news.php';
|
||||
require_once __DIR__ . '/src/perms.php';
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
use Carbon\Carbon;
|
||||
use Misuzu\Application;
|
||||
use Misuzu\Database;
|
||||
use Misuzu\Net\IPAddress;
|
||||
|
||||
require_once __DIR__ . '/../misuzu.php';
|
||||
|
||||
|
@ -169,7 +168,7 @@ switch ($authMode) {
|
|||
break;
|
||||
}
|
||||
|
||||
$ipAddress = remote_address();
|
||||
$ipAddress = ip_remote_address();
|
||||
$emailSent = Database::prepare('
|
||||
SELECT COUNT(`verification_code`) > 0
|
||||
FROM `msz_users_password_resets`
|
||||
|
@ -235,7 +234,7 @@ MSG;
|
|||
$authLoginError = '';
|
||||
|
||||
while ($isSubmission) {
|
||||
$ipAddress = remote_address();
|
||||
$ipAddress = ip_remote_address();
|
||||
|
||||
if (!isset($authUsername, $authPassword)) {
|
||||
$authLoginError = "You didn't fill all the forms!";
|
||||
|
@ -343,7 +342,7 @@ MSG;
|
|||
$authUsername,
|
||||
$authPassword,
|
||||
$authEmail,
|
||||
remote_address()
|
||||
ip_remote_address()
|
||||
);
|
||||
|
||||
if ($createUser < 1) {
|
||||
|
|
|
@ -123,7 +123,7 @@ if ($postRequest) {
|
|||
$topicId,
|
||||
$forum['forum_id'],
|
||||
$app->getUserId(),
|
||||
remote_address(),
|
||||
ip_remote_address(),
|
||||
$postText,
|
||||
MSZ_PARSER_BBCODE
|
||||
);
|
||||
|
|
|
@ -1,221 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu\Net;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* IP Address object.
|
||||
* @package Misuzu\Net
|
||||
* @author flashwave <me@flash.moe>
|
||||
*/
|
||||
final class IPAddress
|
||||
{
|
||||
/**
|
||||
* Default IP Address if $_SERVER['REMOTE_ADDR'] is not set.
|
||||
*/
|
||||
private const FALLBACK_ADDRESS = '::1';
|
||||
|
||||
/**
|
||||
* Fallback version number.
|
||||
*/
|
||||
public const UNKNOWN_VERSION = 0;
|
||||
|
||||
/**
|
||||
* IPv4.
|
||||
*/
|
||||
public const V4 = 4;
|
||||
|
||||
/**
|
||||
* IPv6.
|
||||
*/
|
||||
public const V6 = 6;
|
||||
|
||||
/**
|
||||
* String lengths of expanded IP addresses.
|
||||
*/
|
||||
public const BYTE_COUNT = [
|
||||
self::V4 => 4,
|
||||
self::V6 => 16,
|
||||
];
|
||||
|
||||
/**
|
||||
* IP address version.
|
||||
* @var int
|
||||
*/
|
||||
private $ipVersion = self::UNKNOWN_VERSION;
|
||||
|
||||
/**
|
||||
* Raw IP address.
|
||||
* @var null|string
|
||||
*/
|
||||
private $ipRaw = null;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getVersion(): int
|
||||
{
|
||||
return $this->ipVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRaw(): string
|
||||
{
|
||||
return $this->ipRaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getString(): string
|
||||
{
|
||||
return inet_ntop($this->ipRaw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets GeoIP country for this IP address.
|
||||
* @return string
|
||||
*/
|
||||
public function getCountryCode(): string
|
||||
{
|
||||
return get_country_code($this->getString());
|
||||
}
|
||||
|
||||
/**
|
||||
* IPAddress constructor.
|
||||
* @param int $version
|
||||
* @param string $rawIp
|
||||
*/
|
||||
public function __construct(int $version, string $rawIp)
|
||||
{
|
||||
if (!array_key_exists($version, self::BYTE_COUNT)) {
|
||||
throw new InvalidArgumentException('Invalid IP version provided.');
|
||||
}
|
||||
|
||||
if (strlen($rawIp) !== self::BYTE_COUNT[$version]) {
|
||||
throw new InvalidArgumentException('Binary IP was of invalid length.');
|
||||
}
|
||||
|
||||
$this->ipVersion = $version;
|
||||
$this->ipRaw = $rawIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares one IP to another.
|
||||
* @param IPAddress $other
|
||||
* @return int
|
||||
* @throws InvalidArgumentException If the versions of the IP mismatch.
|
||||
*/
|
||||
public function compareTo(IPAddress $other): int
|
||||
{
|
||||
if ($other->getVersion() !== $this->getVersion()) {
|
||||
throw new InvalidArgumentException('Both addresses must be of the same version.');
|
||||
}
|
||||
|
||||
$parts_this = array_values(unpack('N*', $this->getRaw()));
|
||||
$parts_other = array_values(unpack('N*', $other->getRaw()));
|
||||
$size = count($parts_this);
|
||||
|
||||
if ($size !== count($parts_other)) {
|
||||
throw new InvalidArgumentException('Addresses varied in length. (if you touched $ipRaw, i will fight you)');
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$result = $parts_other[$i] <=> $parts_this[$i];
|
||||
|
||||
if ($result !== 0) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remote address.
|
||||
* @param string $fallbackAddress
|
||||
* @return IPAddress
|
||||
*/
|
||||
public static function remote(string $fallbackAddress = self::FALLBACK_ADDRESS): IPAddress
|
||||
{
|
||||
try {
|
||||
return self::fromString(remote_address($fallbackAddress));
|
||||
} catch (InvalidArgumentException $ex) {
|
||||
return self::fromString($fallbackAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an IPAddress instance from just a raw IP string.
|
||||
* @param string $rawIp
|
||||
* @return IPAddress
|
||||
*/
|
||||
public static function fromRaw(string $rawIp): IPAddress
|
||||
{
|
||||
$version = self::detectVersionFromRaw($rawIp);
|
||||
|
||||
if ($version === self::UNKNOWN_VERSION) {
|
||||
throw new InvalidArgumentException('Invalid raw IP address supplied.');
|
||||
}
|
||||
|
||||
return new static($version, $rawIp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an IPAddress instance from a human readable address string.
|
||||
* @param string $ipAddress
|
||||
* @return IPAddress
|
||||
*/
|
||||
public static function fromString(string $ipAddress): IPAddress
|
||||
{
|
||||
$version = self::detectVersionFromString($ipAddress);
|
||||
|
||||
if (!array_key_exists($version, self::BYTE_COUNT)) {
|
||||
throw new InvalidArgumentException('Invalid IP address supplied.');
|
||||
}
|
||||
|
||||
return new static($version, inet_pton($ipAddress));
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the version of a raw address string.
|
||||
* @param string $rawIp
|
||||
* @return int
|
||||
*/
|
||||
public static function detectVersionFromRaw(string $rawIp): int
|
||||
{
|
||||
$rawLength = strlen($rawIp);
|
||||
|
||||
foreach (self::BYTE_COUNT as $version => $length) {
|
||||
if ($rawLength === $length) {
|
||||
return $version;
|
||||
}
|
||||
}
|
||||
|
||||
return self::UNKNOWN_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the version of a human readable address string.
|
||||
* @param string $ipAddress
|
||||
* @return int
|
||||
*/
|
||||
public static function detectVersionFromString(string $ipAddress): int
|
||||
{
|
||||
if (filter_var($ipAddress, FILTER_VALIDATE_IP) === false) {
|
||||
return self::UNKNOWN_VERSION;
|
||||
}
|
||||
|
||||
if (filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
|
||||
return self::V6;
|
||||
}
|
||||
|
||||
if (filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
|
||||
return self::V4;
|
||||
}
|
||||
|
||||
return self::UNKNOWN_VERSION;
|
||||
}
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu\Net;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class IPAddressRange
|
||||
{
|
||||
/**
|
||||
* @var IPAddress
|
||||
*/
|
||||
private $maskAddress;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $cidrLength;
|
||||
|
||||
/**
|
||||
* @return IPAddress
|
||||
*/
|
||||
public function getMaskAddress(): IPAddress
|
||||
{
|
||||
return $this->maskAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCidrLength(): int
|
||||
{
|
||||
return $this->cidrLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* IPAddressRange constructor.
|
||||
* @param IPAddress $maskAddress
|
||||
* @param int $cidrLength
|
||||
*/
|
||||
public function __construct(IPAddress $maskAddress, int $cidrLength)
|
||||
{
|
||||
if ($cidrLength > IPAddress::BYTE_COUNT[$maskAddress->getVersion()] * 8) {
|
||||
throw new InvalidArgumentException('CIDR length is out of range.');
|
||||
}
|
||||
|
||||
$this->maskAddress = $maskAddress;
|
||||
$this->cidrLength = $cidrLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a conventional <MASKED ADDRESS>/<CIDR LENGTH> format string.
|
||||
* @return string
|
||||
*/
|
||||
public function getMaskedString(): string
|
||||
{
|
||||
return $this->getMaskAddress()->getString() . '/' . $this->getCidrLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches an IPAddress to this range.
|
||||
* @param IPAddress $ipAddress
|
||||
* @param bool $explicitExceptions
|
||||
* @return bool
|
||||
*/
|
||||
public function match(IPAddress $ipAddress, bool $explicitExceptions = false): bool
|
||||
{
|
||||
if ($ipAddress->getVersion() !== $this->getMaskAddress()->getVersion()) {
|
||||
if ($explicitExceptions) {
|
||||
throw new InvalidArgumentException('Both addresses must be of the same version.');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$ipParts = array_values(unpack('N*', $ipAddress->getRaw()));
|
||||
$maskParts = array_values(unpack('N*', $this->getMaskAddress()->getRaw()));
|
||||
$parts = count($ipParts);
|
||||
|
||||
if ($parts !== count($maskParts)) {
|
||||
if ($explicitExceptions) {
|
||||
throw new InvalidArgumentException('Both addresses must be of the same version (failed 1).');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $parts; $i++) {
|
||||
$ipParts[$i] = $ipParts[$i] & $maskParts[$i];
|
||||
}
|
||||
|
||||
return $this->getMaskAddress()->getRaw() === pack('N*', ...$ipParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an IPAddressRange instance using the conventional notation format.
|
||||
* @param string $maskedString
|
||||
* @return IPAddressRange
|
||||
*/
|
||||
public static function fromMaskedString(string $maskedString): IPAddressRange
|
||||
{
|
||||
if (mb_strpos($maskedString, '/') === false) {
|
||||
throw new InvalidArgumentException('Invalid masked string.');
|
||||
}
|
||||
|
||||
[$maskedAddress, $cidrLength] = explode('/', $maskedString, 2);
|
||||
$maskedAddress = IPAddress::fromString($maskedAddress);
|
||||
$cidrLength = (int)$cidrLength;
|
||||
|
||||
return new static($maskedAddress, $cidrLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an IPAddresRange instance from a dash separated range.
|
||||
* I'm very uncertain about the logic here when it comes to addresses larger than 32 bits.
|
||||
* If you do know what you're doing, please review this and call me an idiot.
|
||||
*
|
||||
* @param string $rangeString
|
||||
* @return IPAddressRange
|
||||
*/
|
||||
public static function fromRangeString(string $rangeString): IPAddressRange
|
||||
{
|
||||
if (mb_strpos($rangeString, '-') === false) {
|
||||
throw new InvalidArgumentException('Invalid range string.');
|
||||
}
|
||||
|
||||
[$rangeStart, $rangeEnd] = explode('-', $rangeString, 2);
|
||||
$rangeStart = IPAddress::fromString($rangeStart);
|
||||
$rangeEnd = IPAddress::fromString($rangeEnd);
|
||||
|
||||
// implicitly performs a version compare as well, throws an exception if different
|
||||
if ($rangeStart->compareTo($rangeEnd) < 1) {
|
||||
throw new InvalidArgumentException('Range start was larger (or equal) to the range end.');
|
||||
}
|
||||
|
||||
$partsStart = array_values(unpack('N*', $rangeStart->getRaw()));
|
||||
$partsEnd = array_values(unpack('N*', $rangeEnd->getRaw()));
|
||||
$parts = count($partsStart);
|
||||
|
||||
if ($parts !== count($partsEnd)) {
|
||||
throw new InvalidArgumentException('Range start was larger (or equal) to the range end (failed 1).');
|
||||
}
|
||||
|
||||
$bits = $parts * 32;
|
||||
$mask = array_fill(0, $parts, 0);
|
||||
|
||||
for ($i = 0; $i < $parts; $i++) {
|
||||
$diffs = $partsStart[$i] ^ $partsEnd[$i];
|
||||
|
||||
while ($diffs != 0) {
|
||||
$diffs >>= 1;
|
||||
$bits -= 1;
|
||||
$mask[$i] = ($mask[$i] << 1) | 1;
|
||||
}
|
||||
|
||||
$mask[$i] = $partsStart[$i] & ~$mask[$i];
|
||||
}
|
||||
|
||||
$mask = pack('N*', ...$mask);
|
||||
return new static(new IPAddress($rangeStart->getVersion(), $mask), $bits);
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ function user_login_attempt_record(bool $success, ?int $userId, string $ipAddres
|
|||
|
||||
$storeAttempt->bindValue('was_successful', $success ? 1 : 0);
|
||||
$storeAttempt->bindValue('attempt_ip', $ipAddress);
|
||||
$storeAttempt->bindValue('attempt_country', get_country_code($ipAddress));
|
||||
$storeAttempt->bindValue('attempt_country', ip_country_code($ipAddress));
|
||||
$storeAttempt->bindValue('user_agent', $userAgent);
|
||||
$storeAttempt->bindValue('user_id', $userId, $userId === null ? PDO::PARAM_NULL : PDO::PARAM_INT);
|
||||
$storeAttempt->execute();
|
||||
|
|
|
@ -24,7 +24,7 @@ function user_session_create(
|
|||
');
|
||||
$createSession->bindValue('user_id', $userId);
|
||||
$createSession->bindValue('session_ip', $ipAddress);
|
||||
$createSession->bindValue('session_country', get_country_code($ipAddress));
|
||||
$createSession->bindValue('session_country', ip_country_code($ipAddress));
|
||||
$createSession->bindValue('user_agent', $userAgent);
|
||||
$createSession->bindValue('session_key', $sessionKey);
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ function user_create(
|
|||
$createUser->bindValue('email', $email);
|
||||
$createUser->bindValue('register_ip', $ipAddress);
|
||||
$createUser->bindValue('last_ip', $ipAddress);
|
||||
$createUser->bindValue('user_country', get_country_code($ipAddress));
|
||||
$createUser->bindValue('user_country', ip_country_code($ipAddress));
|
||||
|
||||
return $createUser->execute() ? (int)Database::lastInsertId() : 0;
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ function user_bump_last_active(int $userId, string $ipAddress = null): void
|
|||
`last_ip` = INET6_ATON(:last_ip)
|
||||
WHERE `user_id` = :user_id
|
||||
');
|
||||
$bumpUserLast->bindValue('last_ip', $ipAddress ?? remote_address());
|
||||
$bumpUserLast->bindValue('last_ip', $ipAddress ?? ip_remote_address());
|
||||
$bumpUserLast->bindValue('user_id', $userId);
|
||||
$bumpUserLast->execute();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<?php
|
||||
use Misuzu\Database;
|
||||
use Misuzu\Net\IPAddress;
|
||||
|
||||
function audit_log(
|
||||
string $action,
|
||||
|
@ -8,7 +7,7 @@ function audit_log(
|
|||
array $params = [],
|
||||
?string $ipAddress = null
|
||||
): void {
|
||||
$ipAddress = $ipAddress ?? remote_address();
|
||||
$ipAddress = $ipAddress ?? ip_remote_address();
|
||||
|
||||
for ($i = 0; $i < count($params); $i++) {
|
||||
if (preg_match('#^(-?[0-9]+)$#', $params[$i])) {
|
||||
|
@ -26,7 +25,7 @@ function audit_log(
|
|||
$addLog->bindValue('user', $userId < 1 ? null : $userId);
|
||||
$addLog->bindValue('params', json_encode($params));
|
||||
$addLog->bindValue('ip', $ipAddress);
|
||||
$addLog->bindValue('country', get_country_code($ipAddress));
|
||||
$addLog->bindValue('country', ip_country_code($ipAddress));
|
||||
$addLog->execute();
|
||||
}
|
||||
|
||||
|
|
56
src/ip.php
Normal file
56
src/ip.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
use Misuzu\Application;
|
||||
|
||||
define('MSZ_IP_UNKNOWN', 0);
|
||||
define('MSZ_IP_V4', 4);
|
||||
define('MSZ_IP_V6', 6);
|
||||
|
||||
define('MSZ_IP_SIZES', [
|
||||
MSZ_IP_V4 => 4,
|
||||
MSZ_IP_V6 => 16,
|
||||
]);
|
||||
|
||||
function ip_remote_address(string $fallback = '::1'): string
|
||||
{
|
||||
return $_SERVER['REMOTE_ADDR'] ?? $fallback;
|
||||
}
|
||||
|
||||
function ip_country_code(string $ipAddr, string $fallback = 'XX'): string
|
||||
{
|
||||
try {
|
||||
return Application::geoip()->country($ipAddr)->country->isoCode ?? $fallback;
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
function ip_detect_string_version(string $address): int
|
||||
{
|
||||
if (filter_var($address, FILTER_VALIDATE_IP) === false) {
|
||||
return MSZ_IP_UNKNOWN;
|
||||
}
|
||||
|
||||
if (filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
|
||||
return MSZ_IP_V6;
|
||||
}
|
||||
|
||||
if (filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
|
||||
return MSZ_IP_V4;
|
||||
}
|
||||
|
||||
return MSZ_IP_UNKNOWN;
|
||||
}
|
||||
|
||||
function ip_detect_raw_version(string $raw): int
|
||||
{
|
||||
$rawLength = strlen($raw);
|
||||
|
||||
foreach (MSZ_IP_SIZES as $version => $length) {
|
||||
if ($rawLength === $length) {
|
||||
return $version;
|
||||
}
|
||||
}
|
||||
|
||||
return MSZ_IP_UNKNOWN;
|
||||
}
|
16
utility.php
16
utility.php
|
@ -29,11 +29,6 @@ function array_apply(array $array, callable $func): array
|
|||
return $array;
|
||||
}
|
||||
|
||||
function remote_address(string $fallback = '::1'): string
|
||||
{
|
||||
return $_SERVER['REMOTE_ADDR'] ?? $fallback;
|
||||
}
|
||||
|
||||
function set_cookie_m(string $name, string $value, int $expires): void
|
||||
{
|
||||
setcookie(
|
||||
|
@ -151,17 +146,6 @@ function byte_symbol($bytes, $decimal = false)
|
|||
return sprintf("%.2f %s%sB", $bytes, $symbol, $symbol !== '' && !$decimal ? 'i' : '');
|
||||
}
|
||||
|
||||
function get_country_code(string $ipAddr, string $fallback = 'XX'): string
|
||||
{
|
||||
try {
|
||||
return \Misuzu\Application::geoip()->country($ipAddr)->country->isoCode ?? $fallback;
|
||||
} catch (\Exception $e) {
|
||||
// report error?
|
||||
}
|
||||
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
function get_country_name(string $code): string
|
||||
{
|
||||
switch (strtolower($code)) {
|
||||
|
|
Loading…
Add table
Reference in a new issue