Packages

A wrapper for `NMAP` written in Elixir.

Current section

Files

Jump to
hades lib nmap.ex
Raw

lib/nmap.ex

defmodule Hades.NMAP do
@moduledoc """
Executes and monitors the desired `NMAP` command with the help of `GenServer`.
Terminated or aborted are getting cleaned up with `:trap_exit`.
"""
use GenServer
require Logger
alias Hades.Helpers
@timeout Helpers.hades_timeout()
def execute(args, opts \\ []) do
case args do
{_option, _target} ->
file_path =
GenServer.start_link(__MODULE__, [args] ++ [self()], opts)
|> Hades.NMAP.await()
case file_path do
{:ok, file_path} ->
Helpers.parse_xml(file_path)
{_error, error_msg} ->
{:error, error_msg}
end
_ ->
{:error, "Wrong arguments given."}
end
end
def await(_genserver_return, timeout \\ @timeout) do
receive do
{:finished, file_path} -> {:ok, file_path}
after
timeout -> {:detection_timeout, "Process runned into timeout"}
end
end
def init([{option, target}, self_pid]) do
Process.flag(:trap_exit, true)
path = Helpers.hades_path()
port =
Port.open({:spawn, "nmap #{option} -oX #{path} #{target}"}, [
:binary,
:exit_status
])
Port.monitor(port)
{:ok,
%{
port: port,
file_path: path,
self_pid: self_pid,
latest_output: nil,
exit_status: nil
}}
end
def terminate(reason, %{port: port} = state) do
Logger.info("** TERMINATE: #{inspect(reason)}.")
Logger.info("Final state: #{inspect(state)}")
port_info = Port.info(port)
os_pid = port_info[:os_pid]
Logger.warn("Orphaned OS process: #{os_pid}")
:normal
end
def handle_info({port, {:data, text_line}}, %{port: port} = state) do
Logger.info("NMAP Output: #{inspect(text_line)}")
{:noreply, %{state | latest_output: String.trim(text_line)}}
end
def handle_info({port, {:exit_status, status}}, %{port: port, file_path: file_path} = state) do
Logger.info("Port exit: :exit_status: #{status}")
new_state = %{state | exit_status: status}
send(state.self_pid, {:finished, file_path})
{:noreply, new_state}
end
def handle_info({:DOWN, _ref, :port, port, :normal}, state) do
Logger.info("DOWN message from port: #{inspect(port)}")
{:noreply, state}
end
def handle_info({:EXIT, _port, :normal}, state) do
Logger.info("EXIT")
{:noreply, state}
end
def handle_info(msg, state) do
Logger.info("Unhandled message: #{inspect(msg)}")
{:noreply, state}
end
end