summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2021-01-26 18:31:24 +0100
committerToni Uhlig <matzeton@googlemail.com>2021-01-26 18:31:24 +0100
commit25c23e3d23a3c242a91a17ee0efeda1b1d64c852 (patch)
treea14cd32bc83c164ed4dd9aef67cdac4ab403e066 /examples
parenta94ae79cc4b07bdd90c68deefbc4e7b3fe5fce11 (diff)
Finished (mostly) C guessed/undetected flow dumper.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
Diffstat (limited to 'examples')
-rw-r--r--examples/c-captured/c-captured.c394
-rw-r--r--examples/go-dashboard/main.go2
2 files changed, 360 insertions, 36 deletions
diff --git a/examples/c-captured/c-captured.c b/examples/c-captured/c-captured.c
index 5c7715eff..a7370ccbd 100644
--- a/examples/c-captured/c-captured.c
+++ b/examples/c-captured/c-captured.c
@@ -1,5 +1,6 @@
#include <arpa/inet.h>
#include <errno.h>
+#include <pcap/pcap.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -9,9 +10,46 @@
#include <unistd.h>
#include "nDPIsrvd.h"
-#include "jsmn/jsmn.h"
+#include "utarray.h"
-//#define VERBOSE 1
+//#define VERBOSE
+
+struct packet_data
+{
+ uint64_t packet_ts;
+ size_t packet_len;
+ size_t base64_packet_size;
+ union {
+ char * base64_packet;
+ char const * base64_packet_const;
+ };
+};
+
+struct flow_user_data
+{
+ uint8_t guessed;
+ uint8_t detected;
+ int pkt_datalink;
+ UT_array * packets;
+};
+
+struct callback_tmp_data
+{
+ uint8_t guessed;
+ uint8_t detected;
+ int pkt_datalink;
+
+ uint8_t flow_end_or_idle;
+ uint8_t is_packet_flow;
+
+ struct packet_data pkt;
+};
+
+struct callback_user_data
+{
+ struct nDPIsrvd_flow * flow_table;
+ struct callback_tmp_data tmp;
+};
struct nDPIsrvd_socket * sock = NULL;
static int main_thread_shutdown = 0;
@@ -19,55 +57,322 @@ static char const serv_listen_path[] = DISTRIBUTOR_UNIX_SOCKET;
static char const serv_listen_addr[INET_ADDRSTRLEN] = DISTRIBUTOR_HOST;
static uint16_t const serv_listen_port = DISTRIBUTOR_PORT;
-enum nDPIsrvd_callback_return nDPIsrvd_json_callback(struct nDPIsrvd_socket * const sock, void * user_data)
+static void packet_data_copy(void * dst, const void * src)
+{
+ struct packet_data * const pd_dst = (struct packet_data *)dst;
+ struct packet_data const * const pd_src = (struct packet_data *)src;
+ *pd_dst = *pd_src;
+ if (pd_src->base64_packet != NULL && pd_src->base64_packet_size > 0)
+ {
+ pd_dst->base64_packet = strndup(pd_src->base64_packet, pd_src->base64_packet_size);
+ }
+ else
+ {
+ pd_dst->base64_packet = NULL;
+ pd_dst->base64_packet_size = 0;
+ }
+}
+
+static void packet_data_dtor(void * elt)
+{
+ struct packet_data * const pd_elt = (struct packet_data *)elt;
+ if (pd_elt->base64_packet != NULL)
+ {
+ free(pd_elt->base64_packet);
+ pd_elt->base64_packet = NULL;
+ pd_elt->base64_packet_size = 0;
+ }
+}
+
+static const UT_icd packet_data_icd = {sizeof(struct packet_data), NULL, packet_data_copy, packet_data_dtor};
+
+static char * generate_pcap_filename(struct nDPIsrvd_flow const * const flow,
+ struct flow_user_data const * const flow_user,
+ char * const dest,
+ size_t size)
+{
+ if (flow_user->guessed != 0 || flow_user->detected == 0)
+ {
+ int ret = snprintf(dest, size, "flow-%s-%s.pcap", (flow_user->guessed != 0 ? "guessed" : "undetected"), flow->id);
+ if (ret <= 0 || (size_t)ret > size)
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ return NULL;
+ }
+
+ return dest;
+}
+
+static int packet_write_pcap_file(UT_array const * const pd_array, int pkt_datalink, char const * const filename)
{
- (void)user_data;
+ size_t const max_packet_len = 65535;
+
+ if (pd_array->icd.copy != packet_data_copy || pd_array->icd.dtor != packet_data_dtor)
+ {
+ return 1;
+ }
+
+ if (utarray_len(pd_array) == 0)
+ {
+ printf("no packets received via json, can not dump anything to pcap\n");
+ return 0;
+ }
- if (token_is_start(sock) == 1)
+ pcap_t * p = pcap_open_dead(pkt_datalink, max_packet_len);
+ if (p == NULL)
{
+ return 1;
+ }
+ pcap_dumper_t * pd = pcap_dump_open(p, filename);
+ if (pd == NULL)
+ {
+ fprintf(stderr, "pcap error %s\n", pcap_geterr(p));
+ pcap_close(p);
+ return 1;
+ }
+
+ struct packet_data * pd_elt = (struct packet_data *)utarray_front(pd_array);
+ do
+ {
+ if (pd_elt == NULL)
+ {
+ break;
+ }
+
+ unsigned char pkt_buf[max_packet_len];
+ size_t pkt_buf_len = sizeof(pkt_buf);
+ if (nDPIsrvd_base64decode(pd_elt->base64_packet, pd_elt->base64_packet_size, pkt_buf, &pkt_buf_len) != 0 ||
+ pkt_buf_len == 0)
+ {
+ printf("packet base64 decode failed (%zu bytes): %s\n", pd_elt->base64_packet_size, pd_elt->base64_packet);
+ }
+ else
+ {
+ struct pcap_pkthdr phdr;
+ phdr.ts.tv_sec = 0;
+ phdr.ts.tv_usec = 0;
+ phdr.caplen = pkt_buf_len;
+ phdr.len = pkt_buf_len;
+ pcap_dump((unsigned char *)pd, &phdr, pkt_buf);
+ }
+ } while ((pd_elt = (struct packet_data *)utarray_next(pd_array, pd_elt)) != NULL);
+
+ pcap_dump_close(pd);
+ pcap_close(p);
+
+ return 0;
+}
+
#ifdef VERBOSE
- /* Start of a JSON string. */
- printf("JSON ");
-#endif
+static void packet_data_print(UT_array const * const pd_array)
+{
+ if (pd_array->icd.copy != packet_data_copy || pd_array->icd.dtor != packet_data_dtor)
+ {
+ return;
}
- else if (token_is_end(sock) == 1)
+
+ printf("packet-data array size(): %u\n", pd_array->n);
+ struct packet_data * pd_elt = (struct packet_data *)utarray_front(pd_array);
+ do
{
+ if (pd_elt == NULL)
+ {
+ break;
+ }
+ printf("\tpacket-data base64 length: %zu\n", pd_elt->base64_packet_size);
+ } while ((pd_elt = (struct packet_data *)utarray_next(pd_array, pd_elt)) != NULL);
+}
+#else
+#define packet_data_print(pd_array)
+#endif
+
+enum nDPIsrvd_callback_return nDPIsrvd_json_callback(struct nDPIsrvd_socket * const sock, void * const user_data)
+{
+ struct callback_user_data * const cb_user_data = (struct callback_user_data *)user_data;
+ struct nDPIsrvd_flow * flow = nDPIsrvd_get_flow(sock, &cb_user_data->flow_table, sizeof(struct flow_user_data));
+ struct flow_user_data * flow_user = (struct flow_user_data *)(flow != NULL ? flow->user_data : NULL);
+
+ if (token_is_start(sock) == 1) /* Start of a JSON string. */
+ {
+ memset(&cb_user_data->tmp, 0, sizeof(cb_user_data->tmp));
+ cb_user_data->tmp.pkt_datalink = -1;
#ifdef VERBOSE
- /* End of a JSON string. */
- printf("EoF\n");
+ printf("JSON ");
#endif
+ return CALLBACK_OK;
}
- else if (token_is_key_value_pair(sock) == 1)
+ else if (token_is_end(sock) == 1) /* End of a JSON string. */
{
- if (key_equals(sock, "flow_event_name") == 1)
+ if (flow != NULL)
{
- if (value_equals(sock, "guessed") == 1)
- {
- printf("Guessed flow.\n");
- }
- else if (value_equals(sock, "not-detected") == 1)
+ if (cb_user_data->tmp.is_packet_flow == 1)
{
- printf("Not detected flow.\n");
+ if (flow_user->packets == NULL)
+ {
+ utarray_new(flow_user->packets, &packet_data_icd);
+ }
+ if (flow_user->packets != NULL)
+ {
+ utarray_push_back(flow_user->packets, &cb_user_data->tmp.pkt);
+ }
+ flow_user->pkt_datalink = cb_user_data->tmp.pkt_datalink;
+ } else {
+ if (cb_user_data->tmp.guessed != 0) {
+ flow_user->guessed = cb_user_data->tmp.guessed;
+ }
+ if (cb_user_data->tmp.detected != 0) {
+ flow_user->detected = cb_user_data->tmp.detected;
+ }
}
- else if (value_equals(sock, "detected") == 1)
+ if (cb_user_data->tmp.flow_end_or_idle == 1 &&
+ (flow_user->guessed != 0 || flow_user->detected == 0))
{
- printf("Detected flow.\n");
+ if (flow_user->packets != NULL)
+ {
+ packet_data_print(flow_user->packets);
+ char pcap_filename[64];
+ if (generate_pcap_filename(flow, flow_user, pcap_filename, sizeof(pcap_filename)) == NULL)
+ {
+ fprintf(stderr, "%s\n", "Internal error, exit ..");
+ return CALLBACK_ERROR;
+ }
+ printf("dump flow with id %s to %s\n", flow->id, pcap_filename);
+ if (packet_write_pcap_file(flow_user->packets, flow_user->pkt_datalink, pcap_filename) != 0)
+ {
+ return CALLBACK_ERROR;
+ }
+ utarray_free(flow_user->packets);
+ flow_user->packets = NULL;
+ }
}
+#ifdef VERBOSE
+ printf("GUESSED: %u, DETECTED: %u ", flow_user->guessed, flow_user->detected);
+#endif
}
#ifdef VERBOSE
- printf("[%.*s : %.*s] ",
- sock->jsmn.key_value.key_length,
- sock->jsmn.key_value.key,
- sock->jsmn.key_value.value_length,
- sock->jsmn.key_value.value);
+ printf("EoF\n");
#endif
+ return CALLBACK_OK;
}
- else
+
+ if (token_is_key_value_pair(sock) != 1)
{
fprintf(stderr, "%s\n", "Internal error, exit ..");
return CALLBACK_ERROR;
}
+ if (key_equals(sock, "packet_event_name") == 1)
+ {
+ if (value_equals(sock, "packet-flow") == 1)
+ {
+ cb_user_data->tmp.is_packet_flow = 1;
+ }
+ }
+ else if (key_equals(sock, "pkt") == 1)
+ {
+ cb_user_data->tmp.pkt.base64_packet_const = sock->jsmn.key_value.value;
+ cb_user_data->tmp.pkt.base64_packet_size = sock->jsmn.key_value.value_length;
+ }
+ else if (key_equals(sock, "pkt_ts") == 1)
+ {
+ char * endptr = NULL;
+ unsigned long long int value = strtoull(sock->jsmn.key_value.value, &endptr, 10);
+ if (sock->jsmn.key_value.value == endptr)
+ {
+ fprintf(stderr,
+ "pkt_ts `%.*s': Value `%.*s' is not a valid number.\n",
+ sock->jsmn.key_value.key_length,
+ sock->jsmn.key_value.key,
+ sock->jsmn.key_value.value_length,
+ sock->jsmn.key_value.value);
+ return CALLBACK_ERROR;
+ }
+ if (errno == ERANGE)
+ {
+ fprintf(stderr,
+ "pkt_ts `%.*s': Number too large.\n",
+ sock->jsmn.key_value.key_length,
+ sock->jsmn.key_value.key);
+ return CALLBACK_ERROR;
+ }
+ cb_user_data->tmp.pkt.packet_ts = value;
+ }
+ else if (key_equals(sock, "pkt_len") == 1)
+ {
+ char * endptr = NULL;
+ unsigned long long int value = strtoull(sock->jsmn.key_value.value, &endptr, 10);
+ if (sock->jsmn.key_value.value == endptr)
+ {
+ fprintf(stderr,
+ "pkt_len `%.*s': Value `%.*s' is not a valid number.\n",
+ sock->jsmn.key_value.key_length,
+ sock->jsmn.key_value.key,
+ sock->jsmn.key_value.value_length,
+ sock->jsmn.key_value.value);
+ return CALLBACK_ERROR;
+ }
+ if (errno == ERANGE)
+ {
+ fprintf(stderr,
+ "pkt_len `%.*s': Number too large.\n",
+ sock->jsmn.key_value.key_length,
+ sock->jsmn.key_value.key);
+ return CALLBACK_ERROR;
+ }
+ cb_user_data->tmp.pkt.packet_len = value;
+ }
+ else if (key_equals(sock, "pkt_datalink") == 1)
+ {
+ char * endptr = NULL;
+ unsigned long long int value = strtoull(sock->jsmn.key_value.value, &endptr, 10);
+ if (sock->jsmn.key_value.value == endptr)
+ {
+ fprintf(stderr,
+ "pkt_datalink `%.*s': Value `%.*s' is not a valid number.\n",
+ sock->jsmn.key_value.key_length,
+ sock->jsmn.key_value.key,
+ sock->jsmn.key_value.value_length,
+ sock->jsmn.key_value.value);
+ return CALLBACK_ERROR;
+ }
+ if (errno == ERANGE || value > (unsigned long long int)((uint32_t)-1))
+ {
+ fprintf(stderr,
+ "pkt_datalink `%.*s': Number too large.\n",
+ sock->jsmn.key_value.key_length,
+ sock->jsmn.key_value.key);
+ return CALLBACK_ERROR;
+ }
+ cb_user_data->tmp.pkt_datalink = value;
+ }
+ else if (key_equals(sock, "flow_event_name") == 1)
+ {
+ if (value_equals(sock, "end") == 1 || value_equals(sock, "idle") == 1)
+ {
+ cb_user_data->tmp.flow_end_or_idle = 1;
+ }
+ else if (value_equals(sock, "guessed") == 1)
+ {
+ cb_user_data->tmp.guessed = 1;
+ }
+ else if (value_equals(sock, "detected") == 1)
+ {
+ cb_user_data->tmp.detected = 1;
+ }
+ }
+
+#ifdef VERBOSE
+ printf("[%.*s : %.*s] ",
+ sock->jsmn.key_value.key_length,
+ sock->jsmn.key_value.key,
+ sock->jsmn.key_value.value_length,
+ sock->jsmn.key_value.value);
+#endif
+
return CALLBACK_OK;
}
@@ -83,6 +388,9 @@ static void sighandler(int signum)
int main(int argc, char ** argv)
{
+ struct callback_user_data cb_user_data;
+
+ memset(&cb_user_data, 0, sizeof(cb_user_data));
sock = nDPIsrvd_init();
if (sock == NULL)
{
@@ -100,12 +408,16 @@ int main(int argc, char ** argv)
{
printf("Connecting to UNIX socket: %s\n", argv[1]);
connect_ret = nDPIsrvd_connect_unix(sock, argv[1]);
- } else if (argc == 1) {
+ }
+ else if (argc == 1)
+ {
if (access(serv_listen_path, R_OK) == 0)
{
printf("Connecting to %s\n", serv_listen_path);
connect_ret = nDPIsrvd_connect_unix(sock, serv_listen_path);
- } else {
+ }
+ else
+ {
printf("Connecting to %s:%u\n", serv_listen_addr, serv_listen_port);
connect_ret = nDPIsrvd_connect_ip(sock, serv_listen_addr, serv_listen_port);
}
@@ -114,7 +426,7 @@ int main(int argc, char ** argv)
if (connect_ret != CONNECT_OK)
{
fprintf(stderr, "%s: nDPIsrvd socket connect failed!\n", argv[0]);
- nDPIsrvd_free(&sock);
+ nDPIsrvd_free(&sock, &cb_user_data.flow_table);
return 1;
}
@@ -124,18 +436,30 @@ int main(int argc, char ** argv)
enum nDPIsrvd_read_return read_ret = nDPIsrvd_read(sock);
if (read_ret != READ_OK)
{
+ fprintf(stderr, "%s: nDPIsrvd read failed with: %s\n", argv[0], nDPIsrvd_enum_to_string(read_ret));
break;
}
- enum nDPIsrvd_parse_return parse_ret = nDPIsrvd_parse(sock, nDPIsrvd_json_callback, NULL);
- switch (parse_ret)
+ enum nDPIsrvd_parse_return parse_ret = nDPIsrvd_parse(sock, nDPIsrvd_json_callback, &cb_user_data);
+ if (parse_ret != PARSE_OK)
{
- default:
- break;
+ fprintf(stderr, "%s: nDPIsrvd parse failed with: %s\n", argv[0], nDPIsrvd_enum_to_string(parse_ret));
+ break;
}
}
- nDPIsrvd_free(&sock);
+ struct nDPIsrvd_flow * current_flow;
+ struct nDPIsrvd_flow * tmp;
+ HASH_ITER(hh, cb_user_data.flow_table, current_flow, tmp)
+ {
+ struct flow_user_data * const ud = (struct flow_user_data *)current_flow->user_data;
+ if (ud != NULL && ud->packets != NULL)
+ {
+ utarray_free(ud->packets);
+ ud->packets = NULL;
+ }
+ }
+ nDPIsrvd_free(&sock, &cb_user_data.flow_table);
return 0;
}
diff --git a/examples/go-dashboard/main.go b/examples/go-dashboard/main.go
index 8201ca9df..efd01974b 100644
--- a/examples/go-dashboard/main.go
+++ b/examples/go-dashboard/main.go
@@ -183,7 +183,7 @@ func main() {
break
}
- if jsonStr[jsonLen+nDPIsrvd_JSON_BYTES-1] != '}' {
+ if jsonStr[jsonLen+nDPIsrvd_JSON_BYTES-2] != '}' || jsonStr[jsonLen+nDPIsrvd_JSON_BYTES-1] != '\n' {
ErrorLogger.Printf("BUG: JSON invalid closing character at position %d: '%s'\n",
jsonLen+nDPIsrvd_JSON_BYTES,
string(jsonStr[jsonLen+nDPIsrvd_JSON_BYTES-1]))