2010-12-24 15:13:48 +03:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
"""
|
2011-01-31 14:41:28 +03:00
|
|
|
$Id$
|
2010-12-24 15:13:48 +03:00
|
|
|
|
2011-04-15 16:33:18 +04:00
|
|
|
Copyright (c) 2006-2011 sqlmap developers (http://sqlmap.sourceforge.net/)
|
2010-12-24 15:13:48 +03:00
|
|
|
See the file 'doc/COPYING' for copying permission
|
|
|
|
"""
|
|
|
|
|
2011-01-16 13:52:42 +03:00
|
|
|
import difflib
|
2010-12-24 15:13:48 +03:00
|
|
|
import threading
|
2011-06-17 16:50:28 +04:00
|
|
|
import time
|
2010-12-24 15:13:48 +03:00
|
|
|
|
|
|
|
from lib.core.data import kb
|
2011-05-30 03:17:50 +04:00
|
|
|
from lib.core.data import logger
|
|
|
|
from lib.core.datatype import advancedDict
|
|
|
|
from lib.core.exception import sqlmapThreadException
|
2011-06-07 13:50:00 +04:00
|
|
|
from lib.core.settings import MAX_NUMBER_OF_THREADS
|
2011-06-17 16:50:28 +04:00
|
|
|
from lib.core.settings import PYVERSION
|
2011-05-30 03:17:50 +04:00
|
|
|
|
|
|
|
shared = advancedDict()
|
2010-12-24 15:13:48 +03:00
|
|
|
|
|
|
|
class ThreadData():
|
|
|
|
"""
|
|
|
|
Represents thread independent data
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self):
|
2011-05-30 03:17:50 +04:00
|
|
|
global shared
|
|
|
|
|
2011-04-30 17:20:05 +04:00
|
|
|
self.disableStdOut = False
|
|
|
|
self.lastErrorPage = None
|
|
|
|
self.lastHTTPError = None
|
|
|
|
self.lastRedirectMsg = None
|
|
|
|
self.lastQueryDuration = 0
|
|
|
|
self.lastRequestUID = 0
|
2011-05-11 16:54:33 +04:00
|
|
|
self.retriesCount = 0
|
2011-04-30 17:20:05 +04:00
|
|
|
self.seqMatcher = difflib.SequenceMatcher(None)
|
2011-05-30 03:17:50 +04:00
|
|
|
self.shared = shared
|
2011-04-30 17:20:05 +04:00
|
|
|
self.valueStack = []
|
2010-12-24 15:13:48 +03:00
|
|
|
|
|
|
|
def getCurrentThreadUID():
|
|
|
|
return hash(threading.currentThread())
|
|
|
|
|
2011-06-07 13:50:00 +04:00
|
|
|
def readInput(message, default=None):
|
|
|
|
pass
|
|
|
|
|
2010-12-24 15:13:48 +03:00
|
|
|
def getCurrentThreadData():
|
|
|
|
"""
|
|
|
|
Returns current thread's dependent data
|
|
|
|
"""
|
|
|
|
|
|
|
|
threadUID = getCurrentThreadUID()
|
|
|
|
if threadUID not in kb.threadData:
|
|
|
|
kb.threadData[threadUID] = ThreadData()
|
|
|
|
return kb.threadData[threadUID]
|
2011-05-30 03:17:50 +04:00
|
|
|
|
2011-06-07 13:50:00 +04:00
|
|
|
def exceptionHandledFunction(threadFunction):
|
|
|
|
try:
|
|
|
|
threadFunction()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
kb.threadContinue = False
|
|
|
|
kb.threadException = True
|
|
|
|
raise
|
2011-06-07 14:32:18 +04:00
|
|
|
except Exception, errMsg:
|
|
|
|
# thread is just going to be silently killed
|
|
|
|
print
|
|
|
|
logger.error("thread %s: %s" % (threading.currentThread().getName(), errMsg))
|
2011-06-07 13:50:00 +04:00
|
|
|
|
|
|
|
def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardException=True, threadChoice=False):
|
2011-05-30 03:17:50 +04:00
|
|
|
threads = []
|
|
|
|
|
2011-06-07 13:50:00 +04:00
|
|
|
kb.multiThreadMode = True
|
2011-05-30 03:17:50 +04:00
|
|
|
kb.threadContinue = True
|
|
|
|
kb.threadException = False
|
|
|
|
|
2011-06-07 13:50:00 +04:00
|
|
|
if threadChoice and numThreads == 1:
|
|
|
|
while True:
|
|
|
|
message = "please enter number of threads? [Enter for %d (current)] " % numThreads
|
|
|
|
choice = readInput(message, default=str(numThreads))
|
|
|
|
if choice and choice.isdigit():
|
|
|
|
if int(choice) > MAX_NUMBER_OF_THREADS:
|
|
|
|
errMsg = "maximum number of used threads is %d avoiding possible connection issues" % MAX_NUMBER_OF_THREADS
|
|
|
|
logger.critical(errMsg)
|
|
|
|
else:
|
|
|
|
numThreads = int(choice)
|
|
|
|
break
|
|
|
|
|
|
|
|
if numThreads == 1:
|
|
|
|
warnMsg = "running in a single-thread mode. This could take a while."
|
|
|
|
logger.warn(warnMsg)
|
|
|
|
|
2011-05-30 03:17:50 +04:00
|
|
|
if numThreads > 1:
|
|
|
|
infoMsg = "starting %d threads" % numThreads
|
|
|
|
logger.info(infoMsg)
|
|
|
|
else:
|
|
|
|
threadFunction()
|
|
|
|
return
|
|
|
|
|
|
|
|
# Start the threads
|
|
|
|
for numThread in range(numThreads):
|
2011-06-07 13:50:00 +04:00
|
|
|
thread = threading.Thread(target=exceptionHandledFunction, name=str(numThread), args=[threadFunction])
|
2011-06-17 16:50:28 +04:00
|
|
|
|
|
|
|
# Reference: http://stackoverflow.com/questions/190010/daemon-threads-explanation
|
|
|
|
if PYVERSION >= "2.6":
|
|
|
|
thread.daemon = True
|
|
|
|
else:
|
|
|
|
thread.setDaemon(True)
|
|
|
|
|
2011-05-30 03:17:50 +04:00
|
|
|
thread.start()
|
|
|
|
threads.append(thread)
|
|
|
|
|
|
|
|
# And wait for them to all finish
|
|
|
|
try:
|
|
|
|
alive = True
|
|
|
|
while alive:
|
|
|
|
alive = False
|
|
|
|
for thread in threads:
|
|
|
|
if thread.isAlive():
|
|
|
|
alive = True
|
2011-06-17 16:50:28 +04:00
|
|
|
time.sleep(1)
|
2011-05-30 03:17:50 +04:00
|
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
kb.threadContinue = False
|
|
|
|
kb.threadException = True
|
|
|
|
|
2011-06-20 16:17:19 +04:00
|
|
|
logger.info("waiting for threads to finish (Ctrl+C was pressed)")
|
2011-05-30 03:17:50 +04:00
|
|
|
|
|
|
|
try:
|
|
|
|
while (threading.activeCount() > 1):
|
|
|
|
pass
|
|
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
raise sqlmapThreadException, "user aborted (Ctrl+C was pressed multiple times)"
|
|
|
|
|
2011-05-30 03:56:41 +04:00
|
|
|
if forwardException:
|
|
|
|
raise
|
|
|
|
|
2011-05-30 03:17:50 +04:00
|
|
|
finally:
|
2011-06-07 13:50:00 +04:00
|
|
|
kb.multiThreadMode = False
|
|
|
|
kb.bruteMode = False
|
2011-05-30 03:17:50 +04:00
|
|
|
kb.threadContinue = True
|
|
|
|
kb.threadException = False
|
|
|
|
|
|
|
|
if cleanupFunction:
|
|
|
|
cleanupFunction()
|