2019-05-08 13:47:52 +03:00
|
|
|
#!/usr/bin/env python
|
2011-09-26 00:36:32 +04:00
|
|
|
|
|
|
|
"""
|
2020-01-01 15:25:15 +03:00
|
|
|
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
2017-10-11 15:50:46 +03:00
|
|
|
See the file 'LICENSE' for copying permission
|
2011-09-26 00:36:32 +04:00
|
|
|
"""
|
|
|
|
|
|
|
|
import hashlib
|
2012-07-26 17:42:04 +04:00
|
|
|
import os
|
2011-09-26 00:36:32 +04:00
|
|
|
import sqlite3
|
2011-11-22 14:09:35 +04:00
|
|
|
import threading
|
2012-02-27 15:15:53 +04:00
|
|
|
import time
|
2011-09-26 00:36:32 +04:00
|
|
|
|
2015-09-10 16:51:33 +03:00
|
|
|
from lib.core.common import getSafeExString
|
2011-12-28 17:50:03 +04:00
|
|
|
from lib.core.common import serializeObject
|
2016-02-25 03:20:48 +03:00
|
|
|
from lib.core.common import singleTimeWarnMessage
|
2011-12-28 17:50:03 +04:00
|
|
|
from lib.core.common import unserializeObject
|
2019-03-28 18:04:38 +03:00
|
|
|
from lib.core.compat import xrange
|
2019-05-03 14:20:15 +03:00
|
|
|
from lib.core.convert import getBytes
|
2019-05-06 01:54:21 +03:00
|
|
|
from lib.core.convert import getUnicode
|
2012-09-25 13:21:39 +04:00
|
|
|
from lib.core.data import logger
|
2016-11-09 14:20:54 +03:00
|
|
|
from lib.core.exception import SqlmapConnectionException
|
2014-03-24 13:46:23 +04:00
|
|
|
from lib.core.settings import HASHDB_END_TRANSACTION_RETRIES
|
2012-09-25 13:21:39 +04:00
|
|
|
from lib.core.settings import HASHDB_FLUSH_RETRIES
|
2011-11-22 14:54:29 +04:00
|
|
|
from lib.core.settings import HASHDB_FLUSH_THRESHOLD
|
2016-10-15 01:51:35 +03:00
|
|
|
from lib.core.settings import HASHDB_RETRIEVE_RETRIES
|
2011-09-26 17:36:08 +04:00
|
|
|
from lib.core.threads import getCurrentThreadData
|
2011-11-22 14:09:35 +04:00
|
|
|
from lib.core.threads import getCurrentThreadName
|
2019-04-18 17:06:19 +03:00
|
|
|
from thirdparty import six
|
2011-09-26 00:36:32 +04:00
|
|
|
|
2011-09-26 17:36:08 +04:00
|
|
|
class HashDB(object):
|
2011-09-26 00:36:32 +04:00
|
|
|
def __init__(self, filepath):
|
2011-09-26 17:36:08 +04:00
|
|
|
self.filepath = filepath
|
2011-11-22 14:09:35 +04:00
|
|
|
self._write_cache = {}
|
|
|
|
self._cache_lock = threading.Lock()
|
2011-09-26 17:36:08 +04:00
|
|
|
|
|
|
|
def _get_cursor(self):
|
|
|
|
threadData = getCurrentThreadData()
|
|
|
|
|
|
|
|
if threadData.hashDBCursor is None:
|
2012-11-08 22:16:37 +04:00
|
|
|
try:
|
|
|
|
connection = sqlite3.connect(self.filepath, timeout=3, isolation_level=None)
|
|
|
|
threadData.hashDBCursor = connection.cursor()
|
|
|
|
threadData.hashDBCursor.execute("CREATE TABLE IF NOT EXISTS storage (id INTEGER PRIMARY KEY, value TEXT)")
|
2015-12-30 15:39:08 +03:00
|
|
|
connection.commit()
|
2019-01-22 02:40:48 +03:00
|
|
|
except Exception as ex:
|
2012-11-08 22:16:37 +04:00
|
|
|
errMsg = "error occurred while opening a session "
|
2016-01-12 12:27:04 +03:00
|
|
|
errMsg += "file '%s' ('%s')" % (self.filepath, getSafeExString(ex))
|
2016-11-09 14:20:54 +03:00
|
|
|
raise SqlmapConnectionException(errMsg)
|
2011-09-26 17:36:08 +04:00
|
|
|
|
|
|
|
return threadData.hashDBCursor
|
|
|
|
|
2014-03-24 13:46:23 +04:00
|
|
|
def _set_cursor(self, cursor):
|
|
|
|
threadData = getCurrentThreadData()
|
|
|
|
threadData.hashDBCursor = cursor
|
|
|
|
|
|
|
|
cursor = property(_get_cursor, _set_cursor)
|
2011-09-26 00:36:32 +04:00
|
|
|
|
|
|
|
def close(self):
|
2011-10-25 15:20:42 +04:00
|
|
|
threadData = getCurrentThreadData()
|
2011-09-26 00:36:32 +04:00
|
|
|
try:
|
2011-10-25 15:20:42 +04:00
|
|
|
if threadData.hashDBCursor:
|
|
|
|
threadData.hashDBCursor.close()
|
|
|
|
threadData.hashDBCursor.connection.close()
|
|
|
|
threadData.hashDBCursor = None
|
2011-09-26 00:36:32 +04:00
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
2011-11-01 23:00:34 +04:00
|
|
|
@staticmethod
|
|
|
|
def hashKey(key):
|
2020-05-20 17:11:51 +03:00
|
|
|
key = getBytes(key if isinstance(key, six.text_type) else repr(key), errors="xmlcharrefreplace")
|
2016-04-11 10:43:50 +03:00
|
|
|
retVal = int(hashlib.md5(key).hexdigest(), 16) & 0x7fffffffffffffff # Reference: http://stackoverflow.com/a/4448400
|
2011-09-26 00:36:32 +04:00
|
|
|
return retVal
|
|
|
|
|
2011-12-28 17:50:03 +04:00
|
|
|
def retrieve(self, key, unserialize=False):
|
2011-09-26 00:36:32 +04:00
|
|
|
retVal = None
|
2015-10-24 00:48:41 +03:00
|
|
|
|
2012-07-26 17:42:04 +04:00
|
|
|
if key and (self._write_cache or os.path.isfile(self.filepath)):
|
2011-11-01 23:00:34 +04:00
|
|
|
hash_ = HashDB.hashKey(key)
|
2012-08-21 12:28:25 +04:00
|
|
|
retVal = self._write_cache.get(hash_)
|
2011-11-22 14:54:29 +04:00
|
|
|
if not retVal:
|
2016-10-15 01:51:35 +03:00
|
|
|
for _ in xrange(HASHDB_RETRIEVE_RETRIES):
|
2011-11-22 14:54:29 +04:00
|
|
|
try:
|
|
|
|
for row in self.cursor.execute("SELECT value FROM storage WHERE id=?", (hash_,)):
|
|
|
|
retVal = row[0]
|
2019-01-29 14:30:51 +03:00
|
|
|
except (sqlite3.OperationalError, sqlite3.DatabaseError) as ex:
|
2016-10-15 01:51:35 +03:00
|
|
|
if any(_ in getSafeExString(ex) for _ in ("locked", "no such table")):
|
2016-02-25 03:20:48 +03:00
|
|
|
warnMsg = "problem occurred while accessing session file '%s' ('%s')" % (self.filepath, getSafeExString(ex))
|
|
|
|
singleTimeWarnMessage(warnMsg)
|
2016-10-15 01:51:35 +03:00
|
|
|
elif "Could not decode" in getSafeExString(ex):
|
|
|
|
break
|
|
|
|
else:
|
2019-01-29 14:30:51 +03:00
|
|
|
errMsg = "error occurred while accessing session file '%s' ('%s'). " % (self.filepath, getSafeExString(ex))
|
|
|
|
errMsg += "If the problem persists please rerun with '--flush-session'"
|
|
|
|
raise SqlmapConnectionException(errMsg)
|
2011-11-22 14:54:29 +04:00
|
|
|
else:
|
|
|
|
break
|
2015-10-24 00:48:41 +03:00
|
|
|
|
2016-10-15 01:54:32 +03:00
|
|
|
time.sleep(1)
|
|
|
|
|
2016-10-28 12:52:48 +03:00
|
|
|
if retVal and unserialize:
|
2015-10-24 00:48:41 +03:00
|
|
|
try:
|
|
|
|
retVal = unserializeObject(retVal)
|
|
|
|
except:
|
2016-03-28 20:55:33 +03:00
|
|
|
retVal = None
|
2015-10-24 00:48:41 +03:00
|
|
|
warnMsg = "error occurred while unserializing value for session key '%s'. " % key
|
2018-11-02 18:18:08 +03:00
|
|
|
warnMsg += "If the problem persists please rerun with '--flush-session'"
|
2015-10-24 00:48:41 +03:00
|
|
|
logger.warn(warnMsg)
|
|
|
|
|
|
|
|
return retVal
|
2011-09-26 00:36:32 +04:00
|
|
|
|
2011-12-28 17:50:03 +04:00
|
|
|
def write(self, key, value, serialize=False):
|
2011-09-26 00:36:32 +04:00
|
|
|
if key:
|
2011-11-01 23:00:34 +04:00
|
|
|
hash_ = HashDB.hashKey(key)
|
2011-11-22 14:09:35 +04:00
|
|
|
self._cache_lock.acquire()
|
2012-04-02 16:58:10 +04:00
|
|
|
self._write_cache[hash_] = getUnicode(value) if not serialize else serializeObject(value)
|
2011-11-22 14:09:35 +04:00
|
|
|
self._cache_lock.release()
|
|
|
|
|
|
|
|
if getCurrentThreadName() in ('0', 'MainThread'):
|
|
|
|
self.flush()
|
|
|
|
|
2011-11-22 14:54:29 +04:00
|
|
|
def flush(self, forced=False):
|
2011-11-22 14:41:56 +04:00
|
|
|
if not self._write_cache:
|
|
|
|
return
|
|
|
|
|
2011-11-22 14:54:29 +04:00
|
|
|
if not forced and len(self._write_cache) < HASHDB_FLUSH_THRESHOLD:
|
|
|
|
return
|
|
|
|
|
2011-11-22 14:09:35 +04:00
|
|
|
self._cache_lock.acquire()
|
2011-11-22 16:44:28 +04:00
|
|
|
_ = self._write_cache
|
|
|
|
self._write_cache = {}
|
2011-11-22 14:09:35 +04:00
|
|
|
self._cache_lock.release()
|
|
|
|
|
2011-11-22 15:04:43 +04:00
|
|
|
try:
|
|
|
|
self.beginTransaction()
|
2011-11-22 16:44:28 +04:00
|
|
|
for hash_, value in _.items():
|
2012-09-25 13:21:39 +04:00
|
|
|
retries = 0
|
2011-11-22 15:04:43 +04:00
|
|
|
while True:
|
2011-10-10 18:23:17 +04:00
|
|
|
try:
|
2011-11-22 15:04:43 +04:00
|
|
|
try:
|
|
|
|
self.cursor.execute("INSERT INTO storage VALUES (?, ?)", (hash_, value,))
|
|
|
|
except sqlite3.IntegrityError:
|
|
|
|
self.cursor.execute("UPDATE storage SET value=? WHERE id=?", (value, hash_,))
|
2019-07-25 00:43:08 +03:00
|
|
|
except UnicodeError: # e.g. surrogates not allowed (Issue #3851)
|
|
|
|
break
|
2019-01-22 02:40:48 +03:00
|
|
|
except sqlite3.DatabaseError as ex:
|
2013-02-05 20:04:42 +04:00
|
|
|
if not os.path.exists(self.filepath):
|
|
|
|
debugMsg = "session file '%s' does not exist" % self.filepath
|
|
|
|
logger.debug(debugMsg)
|
|
|
|
break
|
2012-09-25 13:21:39 +04:00
|
|
|
|
|
|
|
if retries == 0:
|
|
|
|
warnMsg = "there has been a problem while writing to "
|
2015-09-10 16:51:33 +03:00
|
|
|
warnMsg += "the session file ('%s')" % getSafeExString(ex)
|
2012-09-25 13:21:39 +04:00
|
|
|
logger.warn(warnMsg)
|
|
|
|
|
|
|
|
if retries >= HASHDB_FLUSH_RETRIES:
|
|
|
|
return
|
2012-02-27 15:15:53 +04:00
|
|
|
else:
|
2012-09-25 13:21:39 +04:00
|
|
|
retries += 1
|
2012-02-27 15:15:53 +04:00
|
|
|
time.sleep(1)
|
2011-11-22 15:04:43 +04:00
|
|
|
else:
|
|
|
|
break
|
|
|
|
finally:
|
|
|
|
self.endTransaction()
|
2011-11-02 13:57:42 +04:00
|
|
|
|
|
|
|
def beginTransaction(self):
|
2011-11-23 18:26:40 +04:00
|
|
|
threadData = getCurrentThreadData()
|
|
|
|
if not threadData.inTransaction:
|
2014-12-05 13:46:03 +03:00
|
|
|
try:
|
|
|
|
self.cursor.execute("BEGIN TRANSACTION")
|
|
|
|
except:
|
|
|
|
# Reference: http://stackoverflow.com/a/25245731
|
|
|
|
self.cursor.close()
|
|
|
|
threadData.hashDBCursor = None
|
|
|
|
self.cursor.execute("BEGIN TRANSACTION")
|
|
|
|
finally:
|
|
|
|
threadData.inTransaction = True
|
2011-11-02 13:57:42 +04:00
|
|
|
|
|
|
|
def endTransaction(self):
|
2011-11-23 18:26:40 +04:00
|
|
|
threadData = getCurrentThreadData()
|
|
|
|
if threadData.inTransaction:
|
2014-03-24 13:46:23 +04:00
|
|
|
retries = 0
|
|
|
|
while retries < HASHDB_END_TRANSACTION_RETRIES:
|
|
|
|
try:
|
|
|
|
self.cursor.execute("END TRANSACTION")
|
|
|
|
threadData.inTransaction = False
|
|
|
|
except sqlite3.OperationalError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
return
|
|
|
|
|
|
|
|
retries += 1
|
|
|
|
time.sleep(1)
|
|
|
|
|
2011-12-21 15:50:49 +04:00
|
|
|
try:
|
2014-03-24 13:46:23 +04:00
|
|
|
self.cursor.execute("ROLLBACK TRANSACTION")
|
2011-12-22 02:59:23 +04:00
|
|
|
except sqlite3.OperationalError:
|
2014-03-24 13:46:23 +04:00
|
|
|
self.cursor.close()
|
|
|
|
self.cursor = None
|
2011-12-21 15:50:49 +04:00
|
|
|
finally:
|
|
|
|
threadData.inTransaction = False
|