increased SQLite connection timeout to 3 seconds, the object will now wait for the lock to go away max 3 seconds, no longer 1 only. Relevant code refactoring and minor improvements all over the API library (issue #297)

This commit is contained in:
Bernardo Damele 2013-01-29 15:38:09 +00:00
parent 9677e0f910
commit 1152cf8958

View File

@ -60,10 +60,10 @@ class Database(object):
def create(self): def create(self):
_, self.database = tempfile.mkstemp(prefix="sqlmapipc-", text=False) _, self.database = tempfile.mkstemp(prefix="sqlmapipc-", text=False)
logger.info("IPC database is %s" % self.database) logger.debug("IPC database: %s" % self.database)
def connect(self): def connect(self):
self.connection = sqlite3.connect(self.database, timeout=1, isolation_level=None) self.connection = sqlite3.connect(self.database, timeout=3, isolation_level=None)
self.cursor = self.connection.cursor() self.cursor = self.connection.cursor()
def disconnect(self): def disconnect(self):
@ -132,18 +132,28 @@ class Task(object):
shutil.rmtree(self.output_directory) shutil.rmtree(self.output_directory)
def engine_start(self): def engine_start(self):
self.process = Popen("python sqlmap.py --pickled-options %s" % base64pickle(self.options), shell=True, stdin=PIPE) self.process = Popen("python sqlmap.py --pickled-options %s" % base64pickle(self.options), shell=True, stdin=PIPE, close_fds=False)
def engine_stop(self): def engine_stop(self):
if self.process: if self.process:
self.process.terminate() return self.process.terminate()
else:
return None
def engine_kill(self): def engine_kill(self):
if self.process: if self.process:
self.process.kill() return self.process.kill()
else:
return None
def engine_get_pid(self): def engine_get_id(self):
return self.processid.pid if self.process:
return self.process.pid
else:
return None
def engine_has_terminated(self):
return isinstance(self.process.returncode, int) == True
# Wrapper functions for sqlmap engine # Wrapper functions for sqlmap engine
class StdDbOut(object): class StdDbOut(object):
@ -162,9 +172,13 @@ class StdDbOut(object):
def write(self, value, status=None, content_type=None): def write(self, value, status=None, content_type=None):
if self.messagetype == "stdout": if self.messagetype == "stdout":
conf.database_cursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", (self.taskid, status, content_type, jsonize(value))) #conf.database_cursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)",
# (self.taskid, status, content_type, base64pickle(value)))
conf.database_cursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)",
(self.taskid, status, content_type, jsonize(value)))
else: else:
conf.database_cursor.execute("INSERT INTO errors VALUES(NULL, ?, ?)", (self.taskid, value)) conf.database_cursor.execute("INSERT INTO errors VALUES(NULL, ?, ?)",
(self.taskid, str(value) if value else ""))
def flush(self): def flush(self):
pass pass
@ -182,8 +196,7 @@ class LogRecorder(logging.StreamHandler):
communication with the parent process communication with the parent process
""" """
conf.database_cursor.execute("INSERT INTO logs VALUES(NULL, ?, ?, ?, ?)", conf.database_cursor.execute("INSERT INTO logs VALUES(NULL, ?, ?, ?, ?)",
(conf.taskid, time.strftime("%X"), record.levelname, (conf.taskid, time.strftime("%X"), record.levelname, record.msg % record.args if record.args else record.msg))
record.msg % record.args if record.args else record.msg))
def setRestAPILog(): def setRestAPILog():
if hasattr(conf, "api"): if hasattr(conf, "api"):
@ -257,35 +270,44 @@ def task_new():
taskid = hexencode(os.urandom(8)) taskid = hexencode(os.urandom(8))
tasks[taskid] = Task(taskid) tasks[taskid] = Task(taskid)
logger.debug("Created new task ID: %s" % taskid)
return jsonize({"taskid": taskid}) return jsonize({"taskid": taskid})
@get("/task/<taskid>/destroy") @get("/task/<taskid>/delete")
def task_destroy(taskid): def task_delete(taskid):
""" """
Destroy own task ID Delete own task ID
""" """
if taskid in tasks: if taskid in tasks:
tasks[taskid].clean_filesystem() tasks[taskid].clean_filesystem()
tasks.pop(taskid) tasks.pop(taskid)
logger.debug("Deleted task ID: %s" % taskid)
return jsonize({"success": True}) return jsonize({"success": True})
else: else:
abort(500, "Invalid task ID") abort(500, "Invalid task ID")
# Admin's methods ###################
@get("/task/<taskid>/list") # Admin functions #
###################
@get("/admin/<taskid>/list")
def task_list(taskid): def task_list(taskid):
""" """
List all active tasks List task poll
""" """
if is_admin(taskid): if is_admin(taskid):
return jsonize({"tasks": tasks}) logger.debug("Listed task poll")
return jsonize({"tasks": tasks, "tasks_num": len(tasks)})
else: else:
abort(401) abort(401)
@get("/task/<taskid>/flush") @get("/admin/<taskid>/flush")
def task_flush(taskid): def task_flush(taskid):
""" """
Flush task spool (destroy all tasks) Flush task spool (delete all tasks)
""" """
global tasks global tasks
@ -294,6 +316,7 @@ def task_flush(taskid):
tasks[task].clean_filesystem() tasks[task].clean_filesystem()
tasks = dict() tasks = dict()
logger.debug("Flushed task poll")
return jsonize({"success": True}) return jsonize({"success": True})
else: else:
abort(401) abort(401)
@ -302,20 +325,7 @@ def task_flush(taskid):
# sqlmap core interact functions # # sqlmap core interact functions #
################################## ##################################
# Admin's methods # Handle task's options
@get("/status/<taskid>")
def status(taskid):
"""
Verify the status of the API as well as the core
"""
if is_admin(taskid):
tasks_num = len(tasks)
return jsonize({"tasks": tasks_num})
else:
abort(401)
# Functions to handle options
@get("/option/<taskid>/list") @get("/option/<taskid>/list")
def option_list(taskid): def option_list(taskid):
""" """
@ -324,7 +334,7 @@ def option_list(taskid):
if taskid not in tasks: if taskid not in tasks:
abort(500, "Invalid task ID") abort(500, "Invalid task ID")
return jsonize(tasks[taskid].get_options()) return jsonize({"options": tasks[taskid].get_options()})
@post("/option/<taskid>/get") @post("/option/<taskid>/get")
def option_get(taskid): def option_get(taskid):
@ -339,7 +349,7 @@ def option_get(taskid):
if option in tasks[taskid]: if option in tasks[taskid]:
return jsonize({option: tasks[taskid].get_option(option)}) return jsonize({option: tasks[taskid].get_option(option)})
else: else:
return jsonize({option: None}) return jsonize({option: "Not set"})
@post("/option/<taskid>/set") @post("/option/<taskid>/set")
def option_set(taskid): def option_set(taskid):
@ -356,7 +366,7 @@ def option_set(taskid):
return jsonize({"success": True}) return jsonize({"success": True})
# Function to handle scans # Handle scans
@post("/scan/<taskid>/start") @post("/scan/<taskid>/start")
def scan_start(taskid): def scan_start(taskid):
""" """
@ -375,12 +385,12 @@ def scan_start(taskid):
tasks[taskid].set_output_directory() tasks[taskid].set_output_directory()
# Launch sqlmap engine in a separate thread # Launch sqlmap engine in a separate thread
logger.debug("starting a scan for task ID %s" % taskid) logger.debug("Starting a scan for task ID %s" % taskid)
# Launch sqlmap engine # Launch sqlmap engine
tasks[taskid].engine_start() tasks[taskid].engine_start()
return jsonize({"success": True}) return jsonize({"success": True, "engineid": tasks[taskid].engine_get_id()})
@get("/scan/<taskid>/stop") @get("/scan/<taskid>/stop")
def scan_stop(taskid): def scan_stop(taskid):
@ -406,21 +416,6 @@ def scan_kill(taskid):
return jsonize({"success": tasks[taskid].engine_kill()}) return jsonize({"success": tasks[taskid].engine_kill()})
@get("/scan/<taskid>/delete")
def scan_delete(taskid):
"""
Delete a scan and corresponding temporary output directory and IPC database
"""
global tasks
if taskid not in tasks:
abort(500, "Invalid task ID")
scan_stop(taskid)
tasks[taskid].clean_filesystem()
return jsonize({"success": True})
@get("/scan/<taskid>/data") @get("/scan/<taskid>/data")
def scan_data(taskid): def scan_data(taskid):
""" """
@ -436,7 +431,8 @@ def scan_data(taskid):
# Read all data from the IPC database for the taskid # Read all data from the IPC database for the taskid
for status, content_type, value in db.execute("SELECT status, content_type, value FROM data WHERE taskid = ? ORDER BY id ASC", (taskid,)): for status, content_type, value in db.execute("SELECT status, content_type, value FROM data WHERE taskid = ? ORDER BY id ASC", (taskid,)):
json_data_message.append([status, content_type, dejsonize(value)]) #json_data_message.append({"status": status, "type": content_type, "value": base64unpickle(value)})
json_data_message.append({"status": status, "type": content_type, "value": dejsonize(value)})
# Read all error messages from the IPC database # Read all error messages from the IPC database
for error in db.execute("SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", (taskid,)): for error in db.execute("SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", (taskid,)):
@ -515,24 +511,26 @@ def server(host="0.0.0.0", port=RESTAPI_SERVER_PORT):
global db global db
adminid = hexencode(os.urandom(16)) adminid = hexencode(os.urandom(16))
logger.info("Running REST-JSON API server at '%s:%d'.." % (host, port))
logger.info("Admin ID: %s" % adminid)
# Initialize IPC database
db = Database() db = Database()
db.initialize() db.initialize()
logger.info("running REST-JSON API server at '%s:%d'.." % (host, port))
logger.info("the admin task ID is: %s" % adminid)
# Run RESTful API # Run RESTful API
run(host=host, port=port, quiet=False, debug=False) run(host=host, port=port, quiet=True, debug=False)
def client(host=RESTAPI_SERVER_HOST, port=RESTAPI_SERVER_PORT): def client(host=RESTAPI_SERVER_HOST, port=RESTAPI_SERVER_PORT):
""" """
REST-JSON API client REST-JSON API client
""" """
addr = "http://%s:%d" % (host, port) addr = "http://%s:%d" % (host, port)
logger.info("starting debug REST-JSON client to '%s'..." % addr) logger.info("Starting REST-JSON API client to '%s'..." % addr)
# TODO: write a simple client with requests, for now use curl from command line # TODO: write a simple client with requests, for now use curl from command line
logger.error("not yet implemented, use curl from command line instead for now, for example:") logger.error("Not yet implemented, use curl from command line instead for now, for example:")
print "\n\t$ curl http://%s:%d/task/new" % (host, port) print "\n\t$ curl http://%s:%d/task/new" % (host, port)
print "\t$ curl -H \"Content-Type: application/json\" -X POST -d '{\"url\": \"http://testphp.vulnweb.com/artists.php?artist=1\"}' http://%s:%d/scan/:taskid/start" % (host, port) print "\t$ curl -H \"Content-Type: application/json\" -X POST -d '{\"url\": \"http://testphp.vulnweb.com/artists.php?artist=1\"}' http://%s:%d/scan/:taskid/start" % (host, port)
print "\t$ curl http://%s:%d/scan/:taskid/output" % (host, port) print "\t$ curl http://%s:%d/scan/:taskid/output" % (host, port)