JSON output support

This was rather simple.
This commit is contained in:
Přemysl Eric Janouch 2014-09-15 00:56:06 +02:00
parent 7d3f0ca4c8
commit 06785ea4e1
3 changed files with 128 additions and 7 deletions

View File

@ -4,7 +4,7 @@ CC = clang
CFLAGS = -std=c99 -Wall -Wextra -Wno-unused-function -ggdb
# -lpthread is only there for debugging (gdb & errno)
# -lrt is only for glibc < 2.17
LDFLAGS = `pkg-config --libs libssl` -lpthread -lrt -ldl -lcurses
LDFLAGS = `pkg-config --libs libssl jansson` -lpthread -lrt -ldl -lcurses
.PHONY: all clean
.SUFFIXES:

8
README
View File

@ -3,12 +3,16 @@ ponymap
`ponymap' is an experimental network scanner, not even in the alpha stage yet.
Replacing nmap is not the goal, even though it would be rather very nice to
have a non-copyleft licensed network scanner.
The ultimate purpose of this scanner is bruteforcing hosts and ports in search
of running services. Replacing nmap is not the goal.
of running services of a kind. It should be very simple and straight-forward
to write your own service detection plugins.
Building and Running
--------------------
Build dependencies: openssl, clang, pkg-config, GNU make, awk, sh
Build dependencies: openssl, clang, pkg-config, GNU make, Jansson
If you don't have Clang, you can edit the Makefile to use GCC or TCC, they work
just as good. But there's no CMake support yet, so I force it in the Makefile.

125
ponymap.c
View File

@ -22,10 +22,13 @@
#include "plugin-api.h"
#include <dirent.h>
#include <dlfcn.h>
#include <arpa/inet.h>
#include <curses.h>
#include <term.h>
#include <jansson.h>
// --- Configuration (application-specific) ------------------------------------
#define DEFAULT_CONNECT_TIMEOUT 10
@ -313,6 +316,9 @@ struct app_context
unsigned connect_timeout; ///< Hard timeout for connect()
unsigned scan_timeout; ///< Hard timeout for service scans
json_t *json_results; ///< The results as a JSON value
const char *json_filename; ///< The filename to write JSON to
SSL_CTX *ssl_ctx; ///< OpenSSL context
struct str_map svc_list; ///< List of services to scan for
@ -376,6 +382,8 @@ app_context_free (struct app_context *self)
if (self->ssl_ctx)
SSL_CTX_free (self->ssl_ctx);
if (self->json_results)
json_decref (self->json_results);
}
// --- Progress indicator ------------------------------------------------------
@ -1052,6 +1060,107 @@ initialize_tls (struct app_context *ctx)
// --- Job generation and result aggregation -----------------------------------
struct target_dump_data
{
char address[INET_ADDRSTRLEN]; ///< The IP address as a string
struct unit **results; ///< Results sorted by service
size_t results_len; ///< Number of results
};
static void
target_dump_json (struct target *self, struct target_dump_data *data)
{
json_t *o = json_object ();
json_array_append_new (self->ctx->json_results, o);
json_object_set_new (o, "address", json_string (data->address));
if (self->hostname)
json_object_set_new (o, "hostname", json_string (self->hostname));
if (self->ctx->quitting)
json_object_set_new (o, "partial", json_boolean (true));
json_t *services = json_array ();
json_object_set_new (o, "services", services);
struct service *last_service = NULL;
json_t *service, *ports;
for (size_t i = 0; i < data->results_len; i++)
{
struct unit *u = data->results[i];
if (u->service != last_service)
{
service = json_object ();
json_array_append_new (services, service);
json_object_set_new (service, "name",
json_string (u->service->name));
json_object_set_new (service, "transport",
json_string (u->transport->name));
json_object_set_new (service, "ports", ports);
last_service = u->service;
ports = json_array ();
}
json_t *port = json_object ();
json_array_append_new (ports, port);
json_object_set_new (port, "port", json_integer (u->port));
json_t *info = json_array ();
json_object_set_new (port, "info", info);
for (size_t k = 0; k < u->info.len; k++)
json_array_append_new (info, json_string (u->info.vector[k]));
}
}
static void
target_dump_terminal (struct target *self, struct target_dump_data *data)
{
// TODO: hide the indicator -> ncurses
// TODO: present the results; if we've been interrupted by the user,
// self->ctx->quitting, state that they're only partial
// TODO: show the indicator again
}
static int
unit_cmp_by_service (const void *ax, const void *bx)
{
const struct unit *a = ax, *b = bx;
return strcmp (a->service->name, b->service->name);
}
static void
target_dump_results (struct target *self)
{
struct app_context *ctx = self->ctx;
struct target_dump_data data;
uint32_t address = htonl (self->ip);
if (!inet_ntop (AF_INET, &address, data.address, sizeof data.address))
{
print_error ("%s: %s", "inet_ntop", strerror (errno));
return;
}
size_t len = 0;
for (struct unit *iter = self->results; iter; iter = iter->next)
len++;
struct unit *sorted[len];
data.results = sorted;
data.results_len = len;
for (struct unit *iter = self->results; iter; iter = iter->next)
sorted[--len] = iter;
// Sort them by service name so that they can be grouped
qsort (sorted, N_ELEMENTS (sorted), sizeof *sorted, unit_cmp_by_service);
if (ctx->json_results)
target_dump_json (self, &data);
target_dump_terminal (self, &data);
}
static struct target *
target_ref (struct target *self)
{
@ -1065,10 +1174,8 @@ target_unref (struct target *self)
if (!self || --self->ref_count)
return;
// TODO: hide the indicator -> ncurses
// TODO: present the results; if we've been interrupted by the user,
// self->ctx->quitting, state that they're only partial
// TODO: show the indicator again
if (self->results)
target_dump_results (self);
// These must have been aborted already (although we could do that in here)
hard_assert (!self->running_units);
@ -1451,6 +1558,8 @@ parse_program_arguments (struct app_context *ctx, int argc, char **argv)
{ 'T', "scan-timeout", "TIMEOUT", 0,
"timeout for service scans, in seconds"
" (default: " XSTRINGIFY (DEFAULT_SCAN_TIMEOUT) ")" },
{ 'j', "json-output", "FILENAME", OPT_LONG_ONLY,
"write the results as JSON" },
{ 'w', "write-default-cfg", "FILENAME",
OPT_OPTIONAL_ARG | OPT_LONG_ONLY,
"write a default configuration file and exit" },
@ -1499,6 +1608,10 @@ parse_program_arguments (struct app_context *ctx, int argc, char **argv)
}
ctx->scan_timeout = ul;
break;
case 'j':
ctx->json_results = json_array ();
ctx->json_filename = optarg;
break;
case 'w':
call_write_default_config (optarg, g_config_table);
exit (EXIT_SUCCESS);
@ -1591,6 +1704,10 @@ main (int argc, char *argv[])
while (ctx.polling)
poller_run (&ctx.poller);
if (ctx.json_results && !json_dump_file (ctx.json_results,
ctx.json_filename, JSON_INDENT (2) | JSON_SORT_KEYS | JSON_ENCODE_ANY))
print_error ("failed to write JSON output");
app_context_free (&ctx);
return EXIT_SUCCESS;
}