diff options
| author | Přemysl Janouch <p.janouch@gmail.com> | 2014-09-15 00:56:06 +0200 | 
|---|---|---|
| committer | Přemysl Janouch <p.janouch@gmail.com> | 2014-09-17 23:22:02 +0200 | 
| commit | 06785ea4e1ace20e014acdfb17634a09cd1c87c4 (patch) | |
| tree | 78c1988c39aa0b130a70199cdc200d17d372b781 | |
| parent | 7d3f0ca4c83fb371aa47b2532199011f32a717fe (diff) | |
| download | ponymap-06785ea4e1ace20e014acdfb17634a09cd1c87c4.tar.gz ponymap-06785ea4e1ace20e014acdfb17634a09cd1c87c4.tar.xz ponymap-06785ea4e1ace20e014acdfb17634a09cd1c87c4.zip | |
JSON output support
This was rather simple.
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | README | 8 | ||||
| -rw-r--r-- | ponymap.c | 125 | 
3 files changed, 128 insertions, 7 deletions
| @@ -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: @@ -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. @@ -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;  } | 
