diff --git a/lib/core/datatype.py b/lib/core/datatype.py index 079222d1a..d32386f06 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -8,6 +8,8 @@ See the file 'LICENSE' for copying permission import copy import types +from thirdparty.odict.odict import OrderedDict + class AttribDict(dict): """ This class defines the sqlmap object, inheriting from Python data @@ -104,3 +106,40 @@ class InjectionDict(AttribDict): self.dbms = None self.dbms_version = None self.os = None + +# Reference: https://www.kunxi.org/2014/05/lru-cache-in-python +class LRUDict(object): + def __init__(self, capacity): + self.capacity = capacity + self.cache = OrderedDict() + + def __len__(self): + return len(self.cache) + + def __contains__(self, key): + return key in self.cache + + def __getitem__(self, key): + try: + value = self.cache.pop(key) + self.cache[key] = value + return value + except KeyError: + return -1 + + def get(self, key): + return self.__getitem__(self, key) + + def __setitem__(self, key, value): + try: + self.cache.pop(key) + except KeyError: + if len(self.cache) >= self.capacity: + self.cache.popitem(last=False) + self.cache[key] = value + + def set(self, key, value): + self.__setitem__(key, value) + + def keys(self): + return self.cache.keys() diff --git a/lib/core/decorators.py b/lib/core/decorators.py index 3ceaa55c9..ecc29d58e 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -7,10 +7,15 @@ See the file 'LICENSE' for copying permission import functools import hashlib +import threading +from lib.core.settings import MAX_CACHE_ITEMS +from lib.core.datatype import LRUDict from lib.core.threads import getCurrentThreadData -def cachedmethod(f, cache={}): +_lock = threading.Lock() + +def cachedmethod(f, cache=LRUDict(capacity=MAX_CACHE_ITEMS)): """ Method with a cached content @@ -19,11 +24,12 @@ def cachedmethod(f, cache={}): @functools.wraps(f) def _(*args, **kwargs): - key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs))).hexdigest(), 16) & 0x7fffffffffffffff - if key not in cache: - cache[key] = f(*args, **kwargs) + with _lock: + key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs))).hexdigest(), 16) & 0x7fffffffffffffff + if key not in cache: + cache[key] = f(*args, **kwargs) - return cache[key] + return cache[key] return _ diff --git a/lib/core/settings.py b/lib/core/settings.py index aa233dfc8..23c824d8e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import DBMS_DIRECTORY_NAME from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.1.80" +VERSION = "1.3.1.81" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) @@ -166,6 +166,9 @@ MAX_TECHNIQUES_PER_VALUE = 2 # In case of missing piece of partial union dump, buffered array must be flushed after certain size MAX_BUFFERED_PARTIAL_UNION_LENGTH = 1024 +# Maximum size of cache used in @cachedmethod decorator +MAX_CACHE_ITEMS = 256 + # Suffix used for naming meta databases in DBMS(es) without explicit database name METADB_SUFFIX = "_masterdb" diff --git a/thirdparty/odict/odict.py b/thirdparty/odict/odict.py index 9b7e9d7be..ee7f2aa24 100644 --- a/thirdparty/odict/odict.py +++ b/thirdparty/odict/odict.py @@ -621,7 +621,7 @@ class _OrderedDict(dict): raise KeyError(key) return val - def popitem(self, i=-1): + def popitem(self, last=True): """ Delete and return an item specified by index, not a random one as in dict. The index is -1 by default (the last item). @@ -643,6 +643,7 @@ class _OrderedDict(dict): if not self._sequence: raise KeyError('popitem(): dictionary is empty') try: + i = -1 if last else 0 key = self._sequence[i] except IndexError: raise IndexError('popitem(): index %s not valid' % i) diff --git a/txt/checksum.md5 b/txt/checksum.md5 index c71c5071b..3e7be18cc 100644 --- a/txt/checksum.md5 +++ b/txt/checksum.md5 @@ -33,8 +33,8 @@ a932126e7d80e545c5d44af178d0bc0c lib/core/bigarray.py f97e6dc62b453cc787ef7f801ee1af5b lib/core/common.py de8d27ae6241163ff9e97aa9e7c51a18 lib/core/convert.py abcb1121eb56d3401839d14e8ed06b6e lib/core/data.py -db60c6ebb63b72ed119e304b359fc1a6 lib/core/datatype.py -b7c912e2af7a3354f6d7c04f556a80b2 lib/core/decorators.py +e1f7758f433202c50426efde5eb96768 lib/core/datatype.py +1646402a733e564f05025e848b323cf9 lib/core/decorators.py 5f4680b769ae07f22157bd832c97cf8f lib/core/defaults.py 9dfc69ba47209a4ceca494dde9ee8183 lib/core/dicts.py 4782353a3072e4d17c4e314daf918d8a lib/core/dump.py @@ -50,7 +50,7 @@ fe370021c6bc99daf44b2bfc0d1effb3 lib/core/patch.py 7d8a22c582ad201f65b73225e4456170 lib/core/replication.py 3179d34f371e0295dd4604568fb30bcd lib/core/revision.py d6269c55789f78cf707e09a0f5b45443 lib/core/session.py -18eeffa589e845f62a7c0452ba3c373a lib/core/settings.py +e9e5e6207591b134c0cf4ecf8e712ea9 lib/core/settings.py 4483b4a5b601d8f1c4281071dff21ecc lib/core/shell.py 10fd19b0716ed261e6d04f311f6f527c lib/core/subprocessng.py 9c7b5c6397fb3da33e7a4d7876d159c6 lib/core/target.py @@ -358,7 +358,7 @@ bf318e0abbe6b2e1a167a233db7f744f thirdparty/magic/magic.py d41d8cd98f00b204e9800998ecf8427e thirdparty/multipart/__init__.py 82432cb4ef575aa16900ba221cc1dc98 thirdparty/multipart/multipartpost.py 3e502b04f3849afbb7f0e13b5fd2b5c1 thirdparty/odict/__init__.py -4174fad6be204761db349032341b7582 thirdparty/odict/odict.py +74048dca0470bc78af73f0aafccfd069 thirdparty/odict/odict.py 0105f1734f326704d2d68839084ca661 thirdparty/oset/_abc.py 54a861de0f08bb80c2e8846579ec83bd thirdparty/oset/__init__.py 6c79e6d14e031beebe6de127b53c7c93 thirdparty/oset/pyoset.py