def append(self, option, argument, label): opt = label ops = NmapOptions() if option is not None and option != "": if argument: ops[option] = argument else: ops[option] = True opt += " (%s)" % join_quoted(ops.render()[1:]) self.list.append([option, argument, opt]) self.options.append(option)
class NmapCommand(object): """This class represents an Nmap command line. It is responsible for starting, stopping, and returning the results from a command-line scan. A command line is represented as a string but it is split into a list of arguments for execution. The normal output (stdout and stderr) are written to the file object self.stdout_file.""" def __init__(self, command): """Initialize an Nmap command. This creates temporary files for redirecting the various types of output and sets the backing command-line string.""" self.command = command self.command_process = None self.stdout_file = None self.ops = NmapOptions() self.ops.parse_string(command) # Replace the executable name with the value of nmap_command_path. self.ops.executable = paths_config.nmap_command_path # Normally we generate a random temporary filename to save XML output # to. If we find -oX or -oA, the user has chosen his own output file. # Set self.xml_is_temp to False and don't delete the file when we're # done. self.xml_is_temp = True self.xml_output_filename = None if self.ops["-oX"]: self.xml_is_temp = False self.xml_output_filename = self.ops["-oX"] if self.ops["-oA"]: self.xml_is_temp = False self.xml_output_filename = self.ops["-oA"] + ".xml" # Escape '%' to avoid strftime expansion. for op in ("-oA", "-oX", "-oG", "-oN", "-oS"): if self.ops[op]: self.ops[op] = escape_nmap_filename(self.ops[op]) if self.xml_is_temp: fh, self.xml_output_filename = tempfile.mkstemp( prefix=APP_NAME + "-", suffix=".xml") os.close(fh) self.ops["-oX"] = escape_nmap_filename(self.xml_output_filename) log.debug(">>> Temporary files:") log.debug(">>> XML OUTPUT: %s" % self.xml_output_filename) def close(self): """Close and remove temporary output files used by the command.""" self.stdout_file.close() if self.xml_is_temp: try: os.remove(self.xml_output_filename) except OSError as e: if e.errno != errno.ENOENT: raise def kill(self): """Kill the nmap subprocess.""" from time import sleep log.debug(">>> Killing scan process %s" % self.command_process.pid) if sys.platform != "win32": try: from signal import SIGTERM, SIGKILL os.kill(self.command_process.pid, SIGTERM) for i in range(10): sleep(0.5) if self.command_process.poll() is not None: # Process has been TERMinated break else: log.debug(">>> SIGTERM has not worked even after waiting for 5 seconds. Using SIGKILL.") # noqa os.kill(self.command_process.pid, SIGKILL) self.command_process.wait() except Exception: pass else: try: import ctypes ctypes.windll.kernel32.TerminateProcess( int(self.command_process._handle), -1) except Exception: pass def get_path(self): """Return a value for the PATH environment variable that is appropriate for the current platform. It will be the PATH from the environment plus possibly some platform-specific directories.""" path_env = os.getenv("PATH") if path_env is None: search_paths = [] else: search_paths = path_env.split(os.pathsep) for path in zenmapCore.Paths.get_extra_executable_search_paths(): if path not in search_paths: search_paths.append(path) return os.pathsep.join(search_paths) def run_scan(self, stderr=None): """Run the command represented by this class.""" # We don't need a file name for stdout output, just a handle. A # TemporaryFile is deleted as soon as it is closed, and in Unix is # unlinked immediately after creation so it's not even visible. f = tempfile.TemporaryFile(mode="rb", prefix=APP_NAME + "-stdout-") self.stdout_file = wrap_file_in_preferred_encoding(f) if stderr is None: stderr = f search_paths = self.get_path() env = dict(os.environ) env["PATH"] = search_paths log.debug("PATH=%s" % env["PATH"]) command_list = self.ops.render() log.debug("Running command: %s" % repr(command_list)) startupinfo = None if sys.platform == "win32": # This keeps a terminal window from opening. startupinfo = subprocess.STARTUPINFO() try: startupinfo.dwFlags |= \ subprocess._subprocess.STARTF_USESHOWWINDOW except AttributeError: # This name is used before Python 2.6.5. startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW self.command_process = subprocess.Popen(command_list, bufsize=1, stdin=subprocess.PIPE, stdout=f, stderr=stderr, startupinfo=startupinfo, env=env) def scan_state(self): """Return the current state of a running scan. A return value of True means the scan is running and a return value of False means the scan subprocess completed successfully. If the subprocess terminated with an error an exception is raised. The scan must have been started with run_scan before calling this method.""" if self.command_process is None: raise Exception("Scan is not running yet!") state = self.command_process.poll() if state is None: return True # True means that the process is still running elif state == 0: return False # False means that the process had a successful exit else: log.warning("An error occurred during the scan execution!") log.warning("Command that raised the exception: '%s'" % self.ops.render_string()) log.warning("Scan output:\n%s" % self.get_output()) raise Exception( "An error occurred during the scan execution!\n\n'%s'" % self.get_output()) def get_output(self): """Return the complete contents of the self.stdout_file. This modifies the file pointer.""" self.stdout_file.seek(0) return self.stdout_file.read() def get_xml_output_filename(self): """Return the name of the XML (-oX) output file.""" return self.xml_output_filename