Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/lib/utils/hashdb.py
3554 views
1
#!/usr/bin/env python
2
3
"""
4
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
5
See the file 'LICENSE' for copying permission
6
"""
7
8
import hashlib
9
import os
10
import sqlite3
11
import struct
12
import threading
13
import time
14
15
from lib.core.common import getSafeExString
16
from lib.core.common import serializeObject
17
from lib.core.common import singleTimeWarnMessage
18
from lib.core.common import unserializeObject
19
from lib.core.compat import xrange
20
from lib.core.convert import getBytes
21
from lib.core.convert import getUnicode
22
from lib.core.data import logger
23
from lib.core.datatype import LRUDict
24
from lib.core.exception import SqlmapConnectionException
25
from lib.core.settings import HASHDB_END_TRANSACTION_RETRIES
26
from lib.core.settings import HASHDB_FLUSH_RETRIES
27
from lib.core.settings import HASHDB_FLUSH_THRESHOLD_ITEMS
28
from lib.core.settings import HASHDB_FLUSH_THRESHOLD_TIME
29
from lib.core.settings import HASHDB_RETRIEVE_RETRIES
30
from lib.core.settings import IS_PYPY
31
from lib.core.threads import getCurrentThreadData
32
from thirdparty import six
33
34
class HashDB(object):
35
def __init__(self, filepath):
36
self.filepath = filepath
37
self._write_cache = {}
38
self._read_cache = LRUDict(capacity=100)
39
self._cache_lock = threading.Lock()
40
self._connections = []
41
self._last_flush_time = time.time()
42
43
def _get_cursor(self):
44
threadData = getCurrentThreadData()
45
46
if threadData.hashDBCursor is None:
47
try:
48
connection = sqlite3.connect(self.filepath, timeout=10, isolation_level=None, check_same_thread=False)
49
if not IS_PYPY:
50
connection.execute("PRAGMA journal_mode=WAL")
51
connection.execute("PRAGMA synchronous=NORMAL")
52
connection.execute("PRAGMA busy_timeout=10000")
53
self._connections.append(connection)
54
threadData.hashDBCursor = connection.cursor()
55
threadData.hashDBCursor.execute("CREATE TABLE IF NOT EXISTS storage (id INTEGER PRIMARY KEY, value TEXT)")
56
connection.commit()
57
except Exception as ex:
58
errMsg = "error occurred while opening a session "
59
errMsg += "file '%s' ('%s')" % (self.filepath, getSafeExString(ex))
60
raise SqlmapConnectionException(errMsg)
61
62
return threadData.hashDBCursor
63
64
def _set_cursor(self, cursor):
65
threadData = getCurrentThreadData()
66
threadData.hashDBCursor = cursor
67
68
cursor = property(_get_cursor, _set_cursor)
69
70
def close(self):
71
threadData = getCurrentThreadData()
72
try:
73
if threadData.hashDBCursor:
74
if self._write_cache:
75
self.flush()
76
77
threadData.hashDBCursor.close()
78
threadData.hashDBCursor.connection.close()
79
threadData.hashDBCursor = None
80
except:
81
pass
82
83
def closeAll(self):
84
if self._write_cache:
85
self.flush()
86
87
for connection in self._connections:
88
try:
89
connection.close()
90
except:
91
pass
92
93
@staticmethod
94
def hashKey(key):
95
key = getBytes(key if isinstance(key, six.text_type) else repr(key), errors="xmlcharrefreplace")
96
retVal = struct.unpack("<Q", hashlib.md5(key).digest()[:8])[0] & 0x7fffffffffffffff
97
return retVal
98
99
def retrieve(self, key, unserialize=False):
100
retVal = None
101
102
if key and (self._write_cache or self._connections or os.path.isfile(self.filepath)):
103
hash_ = HashDB.hashKey(key)
104
retVal = self._write_cache.get(hash_)
105
106
if retVal is None:
107
retVal = self._read_cache.get(hash_)
108
109
if not retVal:
110
for _ in xrange(HASHDB_RETRIEVE_RETRIES):
111
try:
112
for row in self.cursor.execute("SELECT value FROM storage WHERE id=?", (hash_,)):
113
retVal = row[0]
114
except (sqlite3.OperationalError, sqlite3.DatabaseError) as ex:
115
if any(_ in getSafeExString(ex) for _ in ("locked", "no such table")):
116
warnMsg = "problem occurred while accessing session file '%s' ('%s')" % (self.filepath, getSafeExString(ex))
117
singleTimeWarnMessage(warnMsg)
118
elif "Could not decode" in getSafeExString(ex):
119
break
120
else:
121
errMsg = "error occurred while accessing session file '%s' ('%s'). " % (self.filepath, getSafeExString(ex))
122
errMsg += "If the problem persists please rerun with '--flush-session'"
123
raise SqlmapConnectionException(errMsg)
124
else:
125
break
126
127
time.sleep(1)
128
129
if retVal is not None:
130
self._read_cache[hash_] = retVal
131
132
if retVal and unserialize:
133
try:
134
retVal = unserializeObject(retVal)
135
except:
136
retVal = None
137
warnMsg = "error occurred while unserializing value for session key '%s'. " % key
138
warnMsg += "If the problem persists please rerun with '--flush-session'"
139
logger.warning(warnMsg)
140
141
return retVal
142
143
def write(self, key, value, serialize=False):
144
if key:
145
hash_ = HashDB.hashKey(key)
146
with self._cache_lock:
147
self._write_cache[hash_] = self._read_cache[hash_] = getUnicode(value) if not serialize else serializeObject(value)
148
cache_size = len(self._write_cache)
149
time_since_flush = time.time() - self._last_flush_time
150
151
if cache_size >= HASHDB_FLUSH_THRESHOLD_ITEMS or time_since_flush >= HASHDB_FLUSH_THRESHOLD_TIME:
152
self.flush()
153
154
def flush(self):
155
with self._cache_lock:
156
if not self._write_cache:
157
return
158
159
flush_cache = self._write_cache
160
self._write_cache = {}
161
self._last_flush_time = time.time()
162
163
try:
164
self.beginTransaction()
165
for hash_, value in flush_cache.items():
166
retries = 0
167
while True:
168
try:
169
try:
170
self.cursor.execute("INSERT INTO storage VALUES (?, ?)", (hash_, value,))
171
except sqlite3.IntegrityError:
172
self.cursor.execute("UPDATE storage SET value=? WHERE id=?", (value, hash_,))
173
except (UnicodeError, OverflowError): # e.g. surrogates not allowed (Issue #3851)
174
break
175
except sqlite3.DatabaseError as ex:
176
if not os.path.exists(self.filepath):
177
debugMsg = "session file '%s' does not exist" % self.filepath
178
logger.debug(debugMsg)
179
break
180
181
# NOTE: skipping the retries == 0 for graceful resolution of multi-threaded runs
182
if retries == 1:
183
warnMsg = "there has been a problem while writing to "
184
warnMsg += "the session file ('%s')" % getSafeExString(ex)
185
logger.warning(warnMsg)
186
187
if retries >= HASHDB_FLUSH_RETRIES:
188
return
189
else:
190
retries += 1
191
time.sleep(1)
192
else:
193
break
194
finally:
195
self.endTransaction()
196
197
def beginTransaction(self):
198
threadData = getCurrentThreadData()
199
if not threadData.inTransaction:
200
try:
201
self.cursor.execute("BEGIN TRANSACTION")
202
except:
203
try:
204
# Reference: http://stackoverflow.com/a/25245731
205
self.cursor.close()
206
except sqlite3.ProgrammingError:
207
pass
208
threadData.hashDBCursor = None
209
self.cursor.execute("BEGIN TRANSACTION")
210
finally:
211
threadData.inTransaction = True
212
213
def endTransaction(self):
214
threadData = getCurrentThreadData()
215
if threadData.inTransaction:
216
retries = 0
217
while retries < HASHDB_END_TRANSACTION_RETRIES:
218
try:
219
self.cursor.execute("END TRANSACTION")
220
threadData.inTransaction = False
221
except sqlite3.OperationalError:
222
pass
223
except sqlite3.ProgrammingError:
224
self.cursor = None
225
threadData.inTransaction = False
226
return
227
else:
228
return
229
230
retries += 1
231
time.sleep(1)
232
233
try:
234
self.cursor.execute("ROLLBACK TRANSACTION")
235
except sqlite3.OperationalError:
236
self.cursor.close()
237
self.cursor = None
238
finally:
239
threadData.inTransaction = False
240
241