|
|
@@ -13,8 +13,11 @@
|
|
|
|
|
|
namespace PrivateBin\Persistence;
|
|
|
|
|
|
+use Exception;
|
|
|
use IPLib\Factory;
|
|
|
+use IPLib\ParseStringFlag;
|
|
|
use PrivateBin\Configuration;
|
|
|
+use PrivateBin\I18n;
|
|
|
|
|
|
/**
|
|
|
* TrafficLimiter
|
|
|
@@ -24,22 +27,22 @@ use PrivateBin\Configuration;
|
|
|
class TrafficLimiter extends AbstractPersistence
|
|
|
{
|
|
|
/**
|
|
|
- * time limit in seconds, defaults to 10s
|
|
|
+ * listed IPs are the only ones allowed to create, defaults to null
|
|
|
*
|
|
|
* @access private
|
|
|
* @static
|
|
|
- * @var int
|
|
|
+ * @var string|null
|
|
|
*/
|
|
|
- private static $_limit = 10;
|
|
|
+ private static $_creators = null;
|
|
|
|
|
|
/**
|
|
|
- * listed ips are exempted from limits, defaults to null
|
|
|
+ * listed IPs are exempted from limits, defaults to null
|
|
|
*
|
|
|
* @access private
|
|
|
* @static
|
|
|
* @var string|null
|
|
|
*/
|
|
|
- private static $_exemptedIp = null;
|
|
|
+ private static $_exempted = null;
|
|
|
|
|
|
/**
|
|
|
* key to fetch IP address
|
|
|
@@ -51,47 +54,69 @@ class TrafficLimiter extends AbstractPersistence
|
|
|
private static $_ipKey = 'REMOTE_ADDR';
|
|
|
|
|
|
/**
|
|
|
- * set the time limit in seconds
|
|
|
+ * time limit in seconds, defaults to 10s
|
|
|
+ *
|
|
|
+ * @access private
|
|
|
+ * @static
|
|
|
+ * @var int
|
|
|
+ */
|
|
|
+ private static $_limit = 10;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * set configuration options of the traffic limiter
|
|
|
*
|
|
|
* @access public
|
|
|
* @static
|
|
|
- * @param int $limit
|
|
|
+ * @param Configuration $conf
|
|
|
*/
|
|
|
- public static function setLimit($limit)
|
|
|
+ public static function setConfiguration(Configuration $conf)
|
|
|
{
|
|
|
- self::$_limit = $limit;
|
|
|
+ self::setCreators($conf->getKey('creators', 'traffic'));
|
|
|
+ self::setExempted($conf->getKey('exempted', 'traffic'));
|
|
|
+ self::setLimit($conf->getKey('limit', 'traffic'));
|
|
|
+
|
|
|
+ if (($option = $conf->getKey('header', 'traffic')) !== '') {
|
|
|
+ $httpHeader = 'HTTP_' . $option;
|
|
|
+ if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) {
|
|
|
+ self::$_ipKey = $httpHeader;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * set a list of ip(ranges) as string
|
|
|
+ * set a list of creator IP(-ranges) as string
|
|
|
*
|
|
|
* @access public
|
|
|
* @static
|
|
|
- * @param string $exemptedIps
|
|
|
+ * @param string $creators
|
|
|
*/
|
|
|
- public static function setExemptedIp($exemptedIp)
|
|
|
+ public static function setCreators($creators)
|
|
|
{
|
|
|
- self::$_exemptedIp = $exemptedIp;
|
|
|
+ self::$_creators = $creators;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * set configuration options of the traffic limiter
|
|
|
+ * set a list of exempted IP(-ranges) as string
|
|
|
*
|
|
|
* @access public
|
|
|
* @static
|
|
|
- * @param Configuration $conf
|
|
|
+ * @param string $exempted
|
|
|
*/
|
|
|
- public static function setConfiguration(Configuration $conf)
|
|
|
+ public static function setExempted($exempted)
|
|
|
{
|
|
|
- self::setLimit($conf->getKey('limit', 'traffic'));
|
|
|
- self::setExemptedIp($conf->getKey('exemptedIp', 'traffic'));
|
|
|
+ self::$_exempted = $exempted;
|
|
|
+ }
|
|
|
|
|
|
- if (($option = $conf->getKey('header', 'traffic')) !== null) {
|
|
|
- $httpHeader = 'HTTP_' . $option;
|
|
|
- if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) {
|
|
|
- self::$_ipKey = $httpHeader;
|
|
|
- }
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * set the time limit in seconds
|
|
|
+ *
|
|
|
+ * @access public
|
|
|
+ * @static
|
|
|
+ * @param int $limit
|
|
|
+ */
|
|
|
+ public static function setLimit($limit)
|
|
|
+ {
|
|
|
+ self::$_limit = $limit;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -108,7 +133,7 @@ class TrafficLimiter extends AbstractPersistence
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Validate $_ipKey against configured ipranges. If matched we will ignore the ip
|
|
|
+ * validate $_ipKey against configured ipranges. If matched we will ignore the ip
|
|
|
*
|
|
|
* @access private
|
|
|
* @static
|
|
|
@@ -120,8 +145,8 @@ class TrafficLimiter extends AbstractPersistence
|
|
|
if (is_string($ipRange)) {
|
|
|
$ipRange = trim($ipRange);
|
|
|
}
|
|
|
- $address = Factory::addressFromString($_SERVER[self::$_ipKey]);
|
|
|
- $range = Factory::rangeFromString($ipRange);
|
|
|
+ $address = Factory::parseAddressString($_SERVER[self::$_ipKey]);
|
|
|
+ $range = Factory::parseRangeString($ipRange, ParseStringFlag::IPV4_MAYBE_NON_DECIMAL);
|
|
|
|
|
|
// address could not be parsed, we might not be in IP space and try a string comparison instead
|
|
|
if (is_null($address)) {
|
|
|
@@ -136,24 +161,35 @@ class TrafficLimiter extends AbstractPersistence
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * traffic limiter
|
|
|
- *
|
|
|
- * Make sure the IP address makes at most 1 request every 10 seconds.
|
|
|
+ * make sure the IP address is allowed to perfom a request
|
|
|
*
|
|
|
* @access public
|
|
|
* @static
|
|
|
- * @return bool
|
|
|
+ * @throws Exception
|
|
|
+ * @return true
|
|
|
*/
|
|
|
public static function canPass()
|
|
|
{
|
|
|
+ // if creators are defined, the traffic limiter will only allow creation
|
|
|
+ // for these, with no limits, and skip any other rules
|
|
|
+ if (!empty(self::$_creators)) {
|
|
|
+ $creatorIps = explode(',', self::$_creators);
|
|
|
+ foreach ($creatorIps as $ipRange) {
|
|
|
+ if (self::matchIp($ipRange) === true) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw new Exception(I18n::_('Your IP is not authorized to create pastes.'));
|
|
|
+ }
|
|
|
+
|
|
|
// disable limits if set to less then 1
|
|
|
if (self::$_limit < 1) {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- // Check if $_ipKey is exempted from ratelimiting
|
|
|
- if (!is_null(self::$_exemptedIp)) {
|
|
|
- $exIp_array = explode(',', self::$_exemptedIp);
|
|
|
+ // check if $_ipKey is exempted from ratelimiting
|
|
|
+ if (!empty(self::$_exempted)) {
|
|
|
+ $exIp_array = explode(',', self::$_exempted);
|
|
|
foreach ($exIp_array as $ipRange) {
|
|
|
if (self::matchIp($ipRange) === true) {
|
|
|
return true;
|
|
|
@@ -161,7 +197,7 @@ class TrafficLimiter extends AbstractPersistence
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // this hash is used as an array key, hence a shorter algo is used
|
|
|
+ // used as array key, which are limited in length, hence using algo with shorter range
|
|
|
$hash = self::getHash('sha256');
|
|
|
$now = time();
|
|
|
$tl = (int) self::$_store->getValue('traffic_limiter', $hash);
|
|
|
@@ -175,6 +211,12 @@ class TrafficLimiter extends AbstractPersistence
|
|
|
if (!self::$_store->setValue((string) $tl, 'traffic_limiter', $hash)) {
|
|
|
error_log('failed to store the traffic limiter, it probably contains outdated information');
|
|
|
}
|
|
|
- return $result;
|
|
|
+ if ($result) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ throw new Exception(I18n::_(
|
|
|
+ 'Please wait %d seconds between each post.',
|
|
|
+ self::$_limit
|
|
|
+ ));
|
|
|
}
|
|
|
}
|