From 647205177179a6e9ef6c08f08c33a2b405efffbc Mon Sep 17 00:00:00 2001 From: Paul Offord Date: Sun, 20 Jun 2021 17:07:28 +0100 Subject: [PATCH] solved code conflicts pre rebase --- sharkd_session.c | 1100 +++++++++++++++++++++++++++++++++--------- test/suite_sharkd.py | 439 ++++++++++------- 2 files changed, 1147 insertions(+), 392 deletions(-) diff --git a/sharkd_session.c b/sharkd_session.c index 486e45ffdd..81fc577691 100644 --- a/sharkd_session.c +++ b/sharkd_session.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -81,10 +82,11 @@ struct sharkd_filter_item static GHashTable *filter_table = NULL; static int mode; -gboolean extended_log = FALSE; +static guint32 rpcid; static json_dumper dumper = {0}; + static const char * json_find_attr(const char *buf, const jsmntok_t *tokens, int count, const char *attr) { @@ -172,15 +174,532 @@ sharkd_json_array_close(void) } static void -sharkd_json_simple_reply(int err, const char *errmsg) +sharkd_json_response_open(guint32 id) { + json_dumper_begin_object(&dumper); // start the message + sharkd_json_value_string("jsonrpc", "2.0"); + sharkd_json_value_anyf("id", "%d", id); +} + +static void +sharkd_json_response_close(void) +{ + json_dumper_finish(&dumper); + + /* + * We do an explicit fflush after every line, because + * we want output to be written to the socket as soon + * as the line is complete. + * + * The stream is fully-buffered by default, so it's + * only flushed when the buffer fills or the FILE * + * is closed. On UN*X, we could set it to be line + * buffered, but the MSVC standard I/O routines don't + * support line buffering - they only support *byte* + * buffering, doing a write for every byte written, + * which is too inefficient, and full buffering, + * which is what you get if you request line buffering. + */ + fflush(stdout); +} + +static void +sharkd_json_result_prologue(guint32 id) +{ + sharkd_json_response_open(id); + sharkd_json_value_anyf("result", NULL); + json_dumper_begin_object(&dumper); // start the result object +} + +static void +sharkd_json_result_epilogue() +{ + json_dumper_end_object(&dumper); // end the result object + json_dumper_end_object(&dumper); // end the message + sharkd_json_response_close(); +} + +static void +sharkd_json_result_array_prologue(guint32 id) +{ + sharkd_json_response_open(id); + sharkd_json_array_open("result"); // start the result array +} + +static void +sharkd_json_result_array_epilogue() +{ + sharkd_json_array_close(); // end of result array + json_dumper_end_object(&dumper); // end the message + sharkd_json_response_close(); +} + +static void +sharkd_json_simple_ok(guint32 id) +{ + sharkd_json_result_prologue(id); + sharkd_json_value_string("status", "OK"); + sharkd_json_result_epilogue(); +} + +static void +sharkd_json_warning(guint32 id, char *warning) +{ + sharkd_json_result_prologue(id); + sharkd_json_value_string("status", "Warning"); + sharkd_json_value_string("warning", warning); + sharkd_json_result_epilogue(); +} + +static void G_GNUC_PRINTF(4, 5) +sharkd_json_error(guint32 id, int code, char* data, char* format, ...) +{ + sharkd_json_response_open(id); + sharkd_json_value_anyf("error", NULL); json_dumper_begin_object(&dumper); - sharkd_json_value_anyf("err", "%d", err); - if (errmsg) - sharkd_json_value_string("errmsg", errmsg); + sharkd_json_value_anyf("code", "%d", code); + + if (format) + { + // format the text message + va_list args; + + va_start(args, format); + char *error_msg = g_strdup_vprintf(format, args); + va_end(args); + + sharkd_json_value_string("message", error_msg); + + g_free(error_msg); + } json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + + if (data) + sharkd_json_value_string("data", data); + + json_dumper_end_object(&dumper); + sharkd_json_response_close(); +} + +static gboolean +is_param_match(char *param_in, char *valid_param) +{ + char* ptr; + + if (ptr = g_strrstr(valid_param, "*")) + { + int prefix_len = ptr - valid_param; + return !strncmp(param_in, valid_param, prefix_len); + } + else + return !strcmp(param_in, valid_param); +} + +/* +* json_prep does four things: +* +* 1. check the syntax of the root and parameter members +* 2. tokenize the names and values by zero terminating them +* 3. unescape the names and values +* 4. extracts and saves the rpcid +* - we have to do it here as it's needed for the error messages +* +* The objective is to minimise the validation work in the functions +* that process each called method. +* +* This gets a little messy as the JSON parser creates a flat list +* of all members rather than create a tree. +*/ +static gboolean +json_prep(char* buf, const jsmntok_t* tokens, int count) +{ + int i; + char* method = NULL; + char* attr_name = NULL; + char* attr_value = NULL; + +#define SHARKD_JSON_ANY 0 +#define SHARKD_JSON_STRING 1 +#define SHARKD_JSON_INTEGER 2 +#define SHARKD_JSON_UINTEGER 3 +#define SHARKD_JSON_FLOAT 4 +#define SHARKD_JSON_OBJECT 5 +#define SHARKD_JSON_ARRAY 6 +#define SHARKD_JSON_BOOLEAN 7 +#define SHARKD_ARRAY_END 99 + + struct member_attribute { + const char* parent_ctx; + const char* name; + int level; + jsmntype_t type; + int value_type; + gboolean is_mandatory; + }; + +#define MANDATORY TRUE +#define OPTIONAL FALSE + + /* + * The member attribute structure is key to the syntax checking. The + * array contains all of the root level (1) member names, the data + * types permissable for the value and a boolean that indicates whether + * or not the member is mandatory. + * + * Once we get into the next layer (2) of the json tree, we need to check + * params member names and data types dependent in the context of the method + * (parent_ctx). + */ + + struct member_attribute name_array[] = { + // Root members + {NULL, "jsonrpc", 1, JSMN_STRING, SHARKD_JSON_STRING, MANDATORY}, + {NULL, "userid", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {NULL, "id", 1, JSMN_PRIMITIVE, SHARKD_JSON_UINTEGER, MANDATORY}, + {NULL, "method", 1, JSMN_STRING, SHARKD_JSON_STRING, MANDATORY}, + {NULL, "params", 1, JSMN_OBJECT, SHARKD_JSON_OBJECT, OPTIONAL}, + + // Valid methods + {"method", "analyse", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "bye", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "check", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "complete", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "download", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "dumpconf", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "follow", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "frame", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "frames", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "info", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "intervals", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "iograph", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "load", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "setcomment", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "setconf", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "status", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"method", "tap", 1, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + + // Parameters and their method context + {"check", "field", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"check", "filter", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"complete", "field", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"complete", "pref", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"download", "token", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"dumpconf", "pref", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"follow", "follow", 2, JSMN_STRING, SHARKD_JSON_STRING, MANDATORY}, + {"follow", "filter", 2, JSMN_STRING, SHARKD_JSON_STRING, MANDATORY}, + {"frame", "frame", 2, JSMN_PRIMITIVE, SHARKD_JSON_UINTEGER, MANDATORY}, + {"frame", "proto", 2, JSMN_PRIMITIVE, SHARKD_JSON_BOOLEAN, OPTIONAL}, + {"frame", "ref_frame", 2, JSMN_PRIMITIVE, SHARKD_JSON_BOOLEAN, OPTIONAL}, + {"frame", "prev_frame", 2, JSMN_PRIMITIVE, SHARKD_JSON_BOOLEAN, OPTIONAL}, + {"frame", "columns", 2, JSMN_PRIMITIVE, SHARKD_JSON_BOOLEAN, OPTIONAL}, + {"frame", "color", 2, JSMN_PRIMITIVE, SHARKD_JSON_BOOLEAN, OPTIONAL}, + {"frame", "bytes", 2, JSMN_PRIMITIVE, SHARKD_JSON_BOOLEAN, OPTIONAL}, + {"frame", "hidden", 2, JSMN_PRIMITIVE, SHARKD_JSON_BOOLEAN, OPTIONAL}, + {"frames", "column*", 2, JSMN_UNDEFINED, SHARKD_JSON_ANY, OPTIONAL}, + {"frames", "filter", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"frames", "skip", 2, JSMN_PRIMITIVE, SHARKD_JSON_UINTEGER, OPTIONAL}, + {"frames", "limit", 2, JSMN_PRIMITIVE, SHARKD_JSON_UINTEGER, OPTIONAL}, + {"frames", "refs", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"intervals", "interval", 2, JSMN_PRIMITIVE, SHARKD_JSON_UINTEGER, OPTIONAL}, + {"intervals", "filter", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "interval", 2, JSMN_PRIMITIVE, SHARKD_JSON_UINTEGER, OPTIONAL}, + {"iograph", "filter", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "graph0", 2, JSMN_STRING, SHARKD_JSON_STRING, MANDATORY}, + {"iograph", "graph1", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "graph2", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "graph3", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "graph4", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "graph5", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "graph6", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "graph7", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "graph8", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "graph9", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "filter0", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "filter1", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "filter2", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "filter3", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "filter4", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "filter5", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "filter6", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "filter7", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "filter8", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"iograph", "filter9", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"load", "file", 2, JSMN_STRING, SHARKD_JSON_STRING, MANDATORY}, + {"setcomment", "frame", 2, JSMN_PRIMITIVE, SHARKD_JSON_UINTEGER, MANDATORY}, + {"setcomment", "comment", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"setconf", "name", 2, JSMN_STRING, SHARKD_JSON_STRING, MANDATORY}, + {"setconf", "value", 2, JSMN_UNDEFINED, SHARKD_JSON_ANY, MANDATORY}, + {"tap", "tap0", 2, JSMN_STRING, SHARKD_JSON_STRING, MANDATORY}, + {"tap", "tap1", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap2", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap3", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap4", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap5", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap6", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap7", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap8", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap9", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap10", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap11", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap12", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap13", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap14", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + {"tap", "tap15", 2, JSMN_STRING, SHARKD_JSON_STRING, OPTIONAL}, + + // End of the name_array + {NULL, NULL, 0, JSMN_STRING, SHARKD_ARRAY_END, OPTIONAL}, + }; + + rpcid = 0; + + /* sanity check, and split strings */ + if (count < 1 || tokens[0].type != JSMN_OBJECT) + { + sharkd_json_error( + rpcid, -32600, NULL, + "The request must an object" + ); + goto fail; + } + + /* don't need [0] token */ + tokens++; + count--; + + if (count & 1) + { + sharkd_json_error( + rpcid, -32600, NULL, + "The request must contain name/value pairs" + ); + goto fail; + } + + for (i = 0; i < count; i += 2) + { + if (tokens[i].type != JSMN_STRING) + { + sharkd_json_error( + rpcid, -32600, NULL, + "Member names must be a string - member %d is not string", (i / 2) + 1 + ); + goto fail; + } + + buf[tokens[i + 0].end] = '\0'; + buf[tokens[i + 1].end] = '\0'; + + attr_name = &buf[tokens[i + 0].start]; + attr_value = &buf[tokens[i + 1].start]; + + // we must get the id as soon as possible so that it's available in all future error messages + if (!strcmp(attr_name, "id")) + { + if (!ws_strtou32(attr_value, NULL, &rpcid)) + { + sharkd_json_error( + rpcid, -32600, NULL, + "The id value must be a positive integer" + ); + goto fail; + } + } + + if (!strcmp(attr_name, "jsonrpc")) + { + if (strcmp(&buf[tokens[i + 1].start], "2.0")) + { + sharkd_json_error( + rpcid, -32600, NULL, + "Only JSON %s is supported", "2.0" + ); + goto fail; + } + } + + /* unescape only value, as keys are simple strings */ + if (tokens[i + 1].type == JSMN_STRING && !json_decode_string_inplace(attr_value)) + { + sharkd_json_error( + rpcid, -32600, NULL, + "Cannot unescape the value string of member %d", (i / 2) + 1 + ); + goto fail; + } + + /* Confirm that the member is valid */ + gboolean match = FALSE; + + // We need to check root members (level 1) and parameters (level 2), hence the for loop. + + for (int level = 1; level < 3; level++) + { + size_t j = 0; + + while (name_array[j].value_type != SHARKD_ARRAY_END) // iterate through the array until we hit the end + { + if (is_param_match(attr_name, name_array[j].name) && name_array[j].level == level) + { + // We need to be sure the match is in the correct context + // i.e. is this a match for a root member (level 1) or for a parameter (level 2). + + if (level == 1) + { + // need to guard against a parameter name matching a method name + if (method) + { + if (name_array[j].parent_ctx) + { + j++; + continue; + } + + if (!strcmp(method, &buf[tokens[i + 0].start])) + { + j++; + continue; + } + } + + match = TRUE; + } + else if (method) + { + if (level == 2 && !strcmp(name_array[j].parent_ctx, method)) + match = TRUE; + else + { + j++; + continue; + } + } + else + { + j++; + continue; + } + + // The match looks good, let's now check the data types + + if (tokens[i + 1].type != name_array[j].type && name_array[j].type != SHARKD_JSON_ANY) + { + sharkd_json_error( + rpcid, -32600, NULL, + "The data type for member %s is not a valid", attr_name + ); + goto fail; + } + else if (name_array[j].type == JSMN_PRIMITIVE && name_array[j].value_type == SHARKD_JSON_UINTEGER) + { + guint32 temp; + if (!ws_strtou32(attr_value, NULL, &temp) || temp <= 0) + { + sharkd_json_error( + rpcid, -32600, NULL, + "The value for %s must be a positive integer", name_array[j].name + ); + goto fail; + } + } + else if (name_array[j].type == JSMN_PRIMITIVE && name_array[j].value_type == SHARKD_JSON_BOOLEAN) + { + if (!strcmp(attr_value, "true") && !strcmp(attr_value, "false")) + { + sharkd_json_error( + rpcid, -32600, NULL, + "The value for %s must be a boolean (true or false)", name_array[j].name + ); + goto fail; + } + + } + break; // looks like a valid match + } + j++; + } + + if (!strcmp(attr_name, "method")) + { + int k = 0; // name array index + // check that the request method is good + while (name_array[k].value_type != SHARKD_ARRAY_END) + { + if (name_array[k].parent_ctx) + { + if (!strcmp(attr_value, name_array[k].name) && !strcmp(name_array[k].parent_ctx, "method")) + method = attr_value; // the method is valid + } + + k++; + } + + if (!method) + { + sharkd_json_error( + rpcid, -32601, NULL, + "The method %s is not supported", attr_value + ); + goto fail; + } + } + } + + if (!match) + { + sharkd_json_error( + rpcid, -32600, NULL, + "%s is not a valid member name", attr_name + ); + goto fail; + } + } + + /* check for mandatory members */ + size_t j = 0; + + while (name_array[j].value_type != SHARKD_ARRAY_END) + { + if (name_array[j].is_mandatory && name_array[j].level == 1) + { + if (!json_find_attr(buf, tokens, count, name_array[j].name)) + { + sharkd_json_error( + rpcid, -32600, NULL, + "Mandatory member %s is missing", name_array[j].name + ); + goto fail; + } + } + j++; + } + + // check that the current request contains the mandatory parameters + j = 0; + + while (name_array[j].value_type != SHARKD_ARRAY_END) + { + if (name_array[j].is_mandatory && name_array[j].level == 2 && !strcmp(method, name_array[j].parent_ctx)) + { + if (!json_find_attr(buf, tokens, count, name_array[j].name)) + { + sharkd_json_error( + rpcid, -32600, NULL, + "Mandatory parameter %s is missing", name_array[j].name + ); + goto fail; + } + } + j++; + } + + + // check that the parameters for the current request are valid for the method and that the data type for the value is valid + + return TRUE; + +fail: + return FALSE; } static void @@ -433,7 +952,7 @@ sharkd_session_process_info(void) { int i; - json_dumper_begin_object(&dumper); + sharkd_json_result_prologue(rpcid); sharkd_json_array_open("columns"); for (i = 0; i < NUM_COL_FMTS; i++) @@ -523,8 +1042,7 @@ sharkd_session_process_info(void) follow_iterate_followers(sharkd_follower_visit_cb, &i); sharkd_json_array_close(); - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); } /** @@ -551,7 +1069,10 @@ sharkd_session_process_load(const char *buf, const jsmntok_t *tokens, int count) if (sharkd_cf_open(tok_file, WTAP_TYPE_AUTO, FALSE, &err) != CF_OK) { - sharkd_json_simple_reply(err, NULL); + sharkd_json_error( + rpcid, -2001, NULL, + "Unable to open the file" + ); return; } @@ -561,12 +1082,17 @@ sharkd_session_process_load(const char *buf, const jsmntok_t *tokens, int count) } CATCH(OutOfMemoryError) { + sharkd_json_error( + rpcid, -32603, NULL, + "Load failed, out of memory" + ); fprintf(stderr, "load: OutOfMemoryError\n"); err = ENOMEM; } ENDTRY; - sharkd_json_simple_reply(err, NULL); + if (err == 0) + sharkd_json_simple_ok(rpcid); } /** @@ -583,7 +1109,7 @@ sharkd_session_process_load(const char *buf, const jsmntok_t *tokens, int count) static void sharkd_session_process_status(void) { - json_dumper_begin_object(&dumper); + sharkd_json_result_prologue(rpcid); sharkd_json_value_anyf("frames", "%u", cfile.count); sharkd_json_value_anyf("duration", "%.9f", nstime_to_sec(&cfile.elapsed_time)); @@ -604,8 +1130,7 @@ sharkd_session_process_status(void) sharkd_json_value_anyf("filesize", "%" G_GINT64_FORMAT, file_size); } - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); } struct sharkd_analyse_data @@ -671,7 +1196,7 @@ sharkd_session_process_analyse(void) analyser.last_time = NULL; analyser.protocols_set = g_hash_table_new(NULL /* g_direct_hash() */, NULL /* g_direct_equal */); - json_dumper_begin_object(&dumper); + sharkd_json_result_prologue(rpcid); sharkd_json_value_anyf("frames", "%u", cfile.count); @@ -686,8 +1211,7 @@ sharkd_session_process_analyse(void) if (analyser.last_time) sharkd_json_value_anyf("last", "%.9f", nstime_to_sec(analyser.last_time)); - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); g_hash_table_destroy(analyser.protocols_set); } @@ -812,7 +1336,13 @@ sharkd_session_process_frames(const char *buf, const jsmntok_t *tokens, int coun memset(&user_cinfo, 0, sizeof(user_cinfo)); cinfo = sharkd_session_create_columns(&user_cinfo, buf, tokens, count); if (!cinfo) + { + sharkd_json_error( + rpcid, -13001, NULL, + "Column definition invalid - note column 6 requires a custom definition" + ); return; + } } if (tok_filter) @@ -821,7 +1351,14 @@ sharkd_session_process_frames(const char *buf, const jsmntok_t *tokens, int coun filter_item = sharkd_session_filter_data(tok_filter); if (!filter_item) + { + sharkd_json_error( + rpcid, -13002, NULL, + "Filter expression invalid" + ); return; + } + filter_data = filter_item->filtered; } @@ -845,7 +1382,8 @@ sharkd_session_process_frames(const char *buf, const jsmntok_t *tokens, int coun return; } - sharkd_json_array_open(NULL); + sharkd_json_result_array_prologue(rpcid); + for (framenum = 1; framenum <= cfile.count; framenum++) { frame_data *fdata; @@ -932,8 +1470,7 @@ sharkd_session_process_frames(const char *buf, const jsmntok_t *tokens, int coun if (limit && --limit == 0) break; } - sharkd_json_array_close(); - json_dumper_finish(&dumper); + sharkd_json_result_array_epilogue(); if (cinfo != &cfile.cinfo) col_cleanup(cinfo); @@ -2209,8 +2746,11 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) if (!cfg) { - fprintf(stderr, "sharkd_session_process_tap() stat %s not found\n", tok_tap + 5); - continue; + sharkd_json_error( + rpcid, -11001, NULL, + "sharkd_session_process_tap() stat %s not found", tok_tap + 5 + ); + return; } st = stats_tree_new(cfg, NULL, tap_filter); @@ -2246,8 +2786,11 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) analysis = sequence_analysis_find_by_name(tok_tap + 5); if (!analysis) { - fprintf(stderr, "sharkd_session_process_tap() seq analysis %s not found\n", tok_tap + 5); - continue; + sharkd_json_error( + rpcid, -11002, NULL, + "sharkd_session_process_tap() seq analysis %s not found", tok_tap + 5 + ); + return; } graph_analysis = sequence_analysis_info_new(); @@ -2277,8 +2820,11 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) if (!ct || !(tap_func = get_conversation_packet_func(ct))) { - fprintf(stderr, "sharkd_session_process_tap() conv %s not found\n", tok_tap + 5); - continue; + sharkd_json_error( + rpcid, -11003, NULL, + "sharkd_session_process_tap() conv %s not found", tok_tap + 5 + ); + return; } } else if (!strncmp(tok_tap, "endpt:", 6)) @@ -2287,14 +2833,20 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) if (!ct || !(tap_func = get_hostlist_packet_func(ct))) { - fprintf(stderr, "sharkd_session_process_tap() endpt %s not found\n", tok_tap + 6); - continue; + sharkd_json_error( + rpcid, -11004, NULL, + "sharkd_session_process_tap() endpt %s not found", tok_tap + 6 + ); + return; } } else { - fprintf(stderr, "sharkd_session_process_tap() conv/endpt(?): %s not found\n", tok_tap); - continue; + sharkd_json_error( + rpcid, -11005, NULL, + "sharkd_session_process_tap() conv/endpt(?): %s not found", tok_tap + ); + return; } ct_tapname = proto_get_protocol_filter_name(get_conversation_proto_id(ct)); @@ -2319,8 +2871,11 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) if (!stat_tap) { - fprintf(stderr, "sharkd_session_process_tap() nstat=%s not found\n", tok_tap + 6); - continue; + sharkd_json_error( + rpcid, -11006, NULL, + "sharkd_session_process_tap() nstat=%s not found", tok_tap + 6 + ); + return; } stat_tap->stat_tap_init_cb(stat_tap); @@ -2342,16 +2897,22 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) if (!rtd) { - fprintf(stderr, "sharkd_session_process_tap() rtd=%s not found\n", tok_tap + 4); - continue; + sharkd_json_error( + rpcid, -11007, NULL, + "sharkd_session_process_tap() rtd=%s not found", tok_tap + 4 + ); + return; } rtd_table_get_filter(rtd, "", &tap_filter, &err); if (err != NULL) { - fprintf(stderr, "sharkd_session_process_tap() rtd=%s err=%s\n", tok_tap + 4, err); + sharkd_json_error( + rpcid, -11008, NULL, + "sharkd_session_process_tap() rtd=%s err=%s", tok_tap + 4, err + ); g_free(err); - continue; + return; } rtd_data = g_new0(rtd_data_t, 1); @@ -2371,16 +2932,22 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) if (!srt) { - fprintf(stderr, "sharkd_session_process_tap() srt=%s not found\n", tok_tap + 4); - continue; + sharkd_json_error( + rpcid, -11009, NULL, + "sharkd_session_process_tap() srt=%s not found", tok_tap + 4 + ); + return; } srt_table_get_filter(srt, "", &tap_filter, &err); if (err != NULL) { - fprintf(stderr, "sharkd_session_process_tap() srt=%s err=%s\n", tok_tap + 4, err); + sharkd_json_error( + rpcid, -11010, NULL, + "sharkd_session_process_tap() srt=%s err=%s", tok_tap + 4, err + ); g_free(err); - continue; + return; } srt_data = g_new0(srt_data_t, 1); @@ -2401,8 +2968,11 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) if (!eo) { - fprintf(stderr, "sharkd_session_process_tap() eo=%s not found\n", tok_tap + 3); - continue; + sharkd_json_error( + rpcid, -11011, NULL, + "sharkd_session_process_tap() eo=%s not found", tok_tap + 3 + ); + return; } for (object_list = sharkd_eo_list; object_list; object_list = object_list->next) @@ -2465,17 +3035,23 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) } else { - fprintf(stderr, "sharkd_session_process_tap() %s not recognized\n", tok_tap); - continue; + sharkd_json_error( + rpcid, -11012, NULL, + "sharkd_session_process_tap() %s not recognized", tok_tap + ); + return; } if (tap_error) { - fprintf(stderr, "sharkd_session_process_tap() name=%s error=%s", tok_tap, tap_error->str); + sharkd_json_error( + rpcid, -11013, NULL, + "sharkd_session_process_tap() name=%s error=%s", tok_tap, tap_error->str + ); g_string_free(tap_error, TRUE); if (tap_free) tap_free(tap_data); - continue; + return; } taps_data[taps_count] = tap_data; @@ -2485,18 +3061,19 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) fprintf(stderr, "sharkd_session_process_tap() count=%d\n", taps_count); if (taps_count == 0) + { + sharkd_json_result_prologue(rpcid); + sharkd_json_array_open("taps"); + sharkd_json_array_close(); + sharkd_json_result_epilogue(); return; + } - json_dumper_begin_object(&dumper); - + sharkd_json_result_prologue(rpcid); sharkd_json_array_open("taps"); sharkd_retap(); sharkd_json_array_close(); - - sharkd_json_value_anyf("err", "0"); - - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); for (i = 0; i < taps_count; i++) { @@ -2544,13 +3121,13 @@ sharkd_session_process_follow(char *buf, const jsmntok_t *tokens, int count) const char *host; char *port; - if (!tok_follow || !tok_filter) - return; - follower = get_follow_by_name(tok_follow); if (!follower) { - fprintf(stderr, "sharkd_session_process_follow() follower=%s not found\n", tok_follow); + sharkd_json_error( + rpcid, -12001, NULL, + "sharkd_session_process_follow() follower=%s not found", tok_follow + ); return; } @@ -2561,7 +3138,10 @@ sharkd_session_process_follow(char *buf, const jsmntok_t *tokens, int count) tap_error = register_tap_listener(get_follow_tap_string(follower), follow_info, tok_filter, 0, NULL, get_follow_tap_handler(follower), NULL, NULL); if (tap_error) { - fprintf(stderr, "sharkd_session_process_follow() name=%s error=%s", tok_follow, tap_error->str); + sharkd_json_error( + rpcid, -12002, NULL, + "sharkd_session_process_follow() name=%s error=%s", tok_follow, tap_error->str + ); g_string_free(tap_error, TRUE); g_free(follow_info); return; @@ -2569,9 +3149,7 @@ sharkd_session_process_follow(char *buf, const jsmntok_t *tokens, int count) sharkd_retap(); - json_dumper_begin_object(&dumper); - - sharkd_json_value_anyf("err", "0"); + sharkd_json_result_prologue(rpcid); /* Server information: hostname, port, bytes sent */ host = address_to_name(&follow_info->server_ip); @@ -2616,8 +3194,7 @@ sharkd_session_process_follow(char *buf, const jsmntok_t *tokens, int count) sharkd_json_array_close(); } - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); remove_tap_listener(follow_info); follow_info_free(follow_info); @@ -2778,9 +3355,7 @@ sharkd_session_process_frame_cb(epan_dissect_t *edt, proto_tree *tree, struct ep const struct sharkd_frame_request_data * const req_data = (const struct sharkd_frame_request_data * const) data; const gboolean display_hidden = (req_data) ? req_data->display_hidden : FALSE; - json_dumper_begin_object(&dumper); - - sharkd_json_value_anyf("err", "0"); + sharkd_json_result_prologue(rpcid); if (fdata->has_user_comment) pkt_comment = sharkd_get_user_comment(fdata); @@ -2916,8 +3491,7 @@ sharkd_session_process_frame_cb(epan_dissect_t *edt, proto_tree *tree, struct ep follow_iterate_followers(sharkd_follower_visit_layers_cb, pi); sharkd_json_array_close(); - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); } #define SHARKD_IOGRAPH_MAX_ITEMS 250000 /* 250k limit of items is taken from wireshark-qt, on x86_64 sizeof(io_graph_item_t) is 152, so single graph can take max 36 MB */ @@ -3004,13 +3578,7 @@ sharkd_session_process_iograph(char *buf, const jsmntok_t *tokens, int count) int i; if (tok_interval) - { - if (!ws_strtou32(tok_interval, NULL, &interval_ms) || interval_ms == 0) - { - fprintf(stderr, "Invalid interval parameter: %s.\n", tok_interval); - return; - } - } + ws_strtou32(tok_interval, NULL, &interval_ms); for (i = graph_count = 0; i < (int) G_N_ELEMENTS(graphs); i++) { @@ -3070,6 +3638,16 @@ sharkd_session_process_iograph(char *buf, const jsmntok_t *tokens, int count) graph_count++; + if (graph->error) + { + sharkd_json_error( + rpcid, -6001, NULL, + "%s", graph->error->str + ); + g_string_free(graph->error, TRUE); + return; + } + if (graph->error == NULL) is_any_ok = TRUE; } @@ -3078,7 +3656,7 @@ sharkd_session_process_iograph(char *buf, const jsmntok_t *tokens, int count) if (is_any_ok) sharkd_retap(); - json_dumper_begin_object(&dumper); + sharkd_json_result_prologue(rpcid); sharkd_json_array_open("iograph"); for (i = 0; i < graph_count; i++) @@ -3089,8 +3667,9 @@ sharkd_session_process_iograph(char *buf, const jsmntok_t *tokens, int count) if (graph->error) { - sharkd_json_value_string("errmsg", graph->error->str); + fprintf(stderr, "SNAP 6002 - we should never get to here.\n"); g_string_free(graph->error, TRUE); + exit(-1); } else { @@ -3124,8 +3703,7 @@ sharkd_session_process_iograph(char *buf, const jsmntok_t *tokens, int count) } sharkd_json_array_close(); - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); } /** @@ -3172,13 +3750,7 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count) gint64 max_idx = 0; if (tok_interval) - { - if (!ws_strtou32(tok_interval, NULL, &interval_ms) || interval_ms == 0) - { - fprintf(stderr, "Invalid interval parameter: %s.\n", tok_interval); - return; - } - } + ws_strtou32(tok_interval, NULL, &interval_ms); // already validated if (tok_filter) { @@ -3186,7 +3758,13 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count) filter_item = sharkd_session_filter_data(tok_filter); if (!filter_item) + { + sharkd_json_error( + rpcid, -7001, NULL, + "Invalid filter parameter: %s", tok_filter + ); return; + } filter_data = filter_item->filtered; } @@ -3198,7 +3776,7 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count) idx = 0; - json_dumper_begin_object(&dumper); + sharkd_json_result_prologue(rpcid); sharkd_json_array_open("intervals"); start_ts = (cfile.count >= 1) ? &(sharkd_get_frame(1)->abs_ts) : NULL; @@ -3249,8 +3827,7 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count) sharkd_json_value_anyf("frames", "%u", st_total.frames); sharkd_json_value_anyf("bytes", "%" G_GUINT64_FORMAT, st_total.bytes); - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); } /** @@ -3309,16 +3886,35 @@ sharkd_session_process_frame(char *buf, const jsmntok_t *tokens, int count) guint32 dissect_flags = SHARKD_DISSECT_FLAG_NULL; struct sharkd_frame_request_data req_data; - if (!tok_frame || !ws_strtou32(tok_frame, NULL, &framenum) || framenum == 0) - return; + ws_strtou32(tok_frame, NULL, &framenum); // we have already validated this ref_frame_num = (framenum != 1) ? 1 : 0; - if (tok_ref_frame && (!ws_strtou32(tok_ref_frame, NULL, &ref_frame_num) || ref_frame_num > framenum)) - return; + if (tok_ref_frame) + { + ws_strtou32(tok_ref_frame, NULL, &ref_frame_num); + if (ref_frame_num > framenum) + { + sharkd_json_error( + rpcid, -8001, NULL, + "Invalid ref_frame - The ref_frame occurs after the frame specified" + ); + return; + } + } prev_dis_num = framenum - 1; - if (tok_prev_frame && (!ws_strtou32(tok_prev_frame, NULL, &prev_dis_num) || prev_dis_num >= framenum)) - return; + if (tok_prev_frame) + { + ws_strtou32(tok_prev_frame, NULL, &prev_dis_num); + if (prev_dis_num >= framenum) + { + sharkd_json_error( + rpcid, -8002, NULL, + "Invalid prev_frame - The prev_frame occurs on or after the frame specified" + ); + return; + } + } if (json_find_attr(buf, tokens, count, "proto") != NULL) dissect_flags |= SHARKD_DISSECT_FLAG_PROTO_TREE; @@ -3331,7 +3927,14 @@ sharkd_session_process_frame(char *buf, const jsmntok_t *tokens, int count) req_data.display_hidden = (json_find_attr(buf, tokens, count, "v") != NULL); - sharkd_dissect_request(framenum, ref_frame_num, prev_dis_num, &sharkd_session_process_frame_cb, dissect_flags, &req_data); + if (sharkd_dissect_request(framenum, ref_frame_num, prev_dis_num, &sharkd_session_process_frame_cb, dissect_flags, &req_data)) + { + sharkd_json_error( + rpcid, -8003, NULL, + "Invalid frame - The frame number requested is out of range" + ); + return; + } } /** @@ -3354,9 +3957,6 @@ sharkd_session_process_check(char *buf, const jsmntok_t *tokens, int count) const char *tok_filter = json_find_attr(buf, tokens, count, "filter"); const char *tok_field = json_find_attr(buf, tokens, count, "field"); - json_dumper_begin_object(&dumper); - sharkd_json_value_anyf("err", "0"); - if (tok_filter != NULL) { char *err_msg = NULL; @@ -3364,18 +3964,22 @@ sharkd_session_process_check(char *buf, const jsmntok_t *tokens, int count) if (dfilter_compile(tok_filter, &dfp, &err_msg)) { - const char *s = "ok"; - if (dfp && dfilter_deprecated_tokens(dfp)) - s = "warn"; + sharkd_json_warning(rpcid, err_msg); + else + sharkd_json_simple_ok(rpcid); - sharkd_json_value_string("filter", s); dfilter_free(dfp); + g_free(err_msg); + return 0; } else { - sharkd_json_value_string("filter", err_msg); - g_free(err_msg); + sharkd_json_error( + rpcid, -5001, NULL, + "Filter invalid - %s", err_msg + ); + return -5001; } } @@ -3383,12 +3987,22 @@ sharkd_session_process_check(char *buf, const jsmntok_t *tokens, int count) { header_field_info *hfi = proto_registrar_get_byname(tok_field); - sharkd_json_value_string("field", (hfi) ? "ok" : "notfound"); + if (!hfi) + { + sharkd_json_error( + rpcid, -5002, NULL, + "Field %s not found", tok_field + ); + return -5002; + } + else + { + sharkd_json_simple_ok(rpcid); + return 0; + } } - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); - + sharkd_json_simple_ok(rpcid); return 0; } @@ -3457,8 +4071,7 @@ sharkd_session_process_complete(char *buf, const jsmntok_t *tokens, int count) const char *tok_field = json_find_attr(buf, tokens, count, "field"); const char *tok_pref = json_find_attr(buf, tokens, count, "pref"); - json_dumper_begin_object(&dumper); - sharkd_json_value_anyf("err", "0"); + sharkd_json_result_prologue(rpcid); if (tok_field != NULL && tok_field[0]) { @@ -3553,8 +4166,7 @@ sharkd_session_process_complete(char *buf, const jsmntok_t *tokens, int count) sharkd_json_array_close(); } - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); return 0; } @@ -3582,15 +4194,34 @@ sharkd_session_process_setcomment(char *buf, const jsmntok_t *tokens, int count) int ret; if (!tok_frame || !ws_strtou32(tok_frame, NULL, &framenum) || framenum == 0) + { + sharkd_json_error( + rpcid, -3001, NULL, + "Frame number must be a positive integer" + ); return; + } - fdata = sharkd_get_frame(framenum); + fdata = sharkd_get_frame(framenum); // BUG HERE - If no file loaded you get a crash if (!fdata) + { + sharkd_json_error( + rpcid, -3002, NULL, + "Frame number is out of range" + ); return; + } ret = sharkd_set_user_comment(fdata, tok_comment); - sharkd_json_simple_reply(ret, NULL); + if (ret) + sharkd_json_error( + rpcid, -3003, NULL, + "Unable to set the comment" + ); + else + sharkd_json_simple_ok(rpcid); + } /** @@ -3615,14 +4246,55 @@ sharkd_session_process_setconf(char *buf, const jsmntok_t *tokens, int count) prefs_set_pref_e ret; - if (!tok_name || tok_name[0] == '\0' || !tok_value) + if (!tok_name || tok_name[0] == '\0') + { + sharkd_json_error( + rpcid, -4001, NULL, + "Preference name missing" + ); return; + } + + if (!tok_value) + { + sharkd_json_error( + rpcid, -4002, NULL, + "Preference value missing" + ); + return; + } snprintf(pref, sizeof(pref), "%s:%s", tok_name, tok_value); ret = prefs_set_pref(pref, &errmsg); - sharkd_json_simple_reply(ret, errmsg); + switch (ret) + { + case PREFS_SET_OK: + sharkd_json_simple_ok(rpcid); + break; + + case PREFS_SET_OBSOLETE: + sharkd_json_error( + rpcid, -4003, NULL, + "The preference specified is obsolete" + ); + break; + + case PREFS_SET_NO_SUCH_PREF: + sharkd_json_error( + rpcid, -4004, NULL, + "No such preference exists" + ); + break; + + default: + sharkd_json_error( + rpcid, -4005, NULL, + "Unable to set the preference" + ); + } + g_free(errmsg); } @@ -3781,15 +4453,14 @@ sharkd_session_process_dumpconf(char *buf, const jsmntok_t *tokens, int count) data.module = NULL; - json_dumper_begin_object(&dumper); + sharkd_json_result_prologue(rpcid); sharkd_json_value_anyf("prefs", NULL); json_dumper_begin_object(&dumper); prefs_modules_foreach(sharkd_session_process_dumpconf_mod_cb, &data); json_dumper_end_object(&dumper); - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); return; } @@ -3809,18 +4480,25 @@ sharkd_session_process_dumpconf(char *buf, const jsmntok_t *tokens, int count) data.module = pref_mod; - json_dumper_begin_object(&dumper); + sharkd_json_result_prologue(rpcid); sharkd_json_value_anyf("prefs", NULL); json_dumper_begin_object(&dumper); sharkd_session_process_dumpconf_cb(pref, &data); json_dumper_end_object(&dumper); - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); + return; + } + else + { + sharkd_json_error( + rpcid, -9001, NULL, + "Invalid pref %s.", tok_pref + ); + return; } - return; } pref_mod = prefs_find_module(tok_pref); @@ -3830,15 +4508,21 @@ sharkd_session_process_dumpconf(char *buf, const jsmntok_t *tokens, int count) data.module = pref_mod; - json_dumper_begin_object(&dumper); + sharkd_json_result_prologue(rpcid); sharkd_json_value_anyf("prefs", NULL); json_dumper_begin_object(&dumper); prefs_pref_foreach(pref_mod, sharkd_session_process_dumpconf_cb, &data); json_dumper_end_object(&dumper); - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); + } + else + { + sharkd_json_error( + rpcid, -9002, NULL, + "Invalid pref %s.", tok_pref + ); } } @@ -4068,12 +4752,16 @@ sharkd_session_process_download(char *buf, const jsmntok_t *tokens, int count) const char *mime = (eo_entry->content_type) ? eo_entry->content_type : "application/octet-stream"; const char *filename = (eo_entry->filename) ? eo_entry->filename : tok_token; - json_dumper_begin_object(&dumper); + sharkd_json_result_prologue(rpcid); sharkd_json_value_string("file", filename); sharkd_json_value_string("mime", mime); sharkd_json_value_base64("data", eo_entry->payload_data, eo_entry->payload_len); - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); + } + else + { + sharkd_json_result_prologue(rpcid); + sharkd_json_result_epilogue(); } } else if (!strcmp(tok_token, "ssl-secrets")) @@ -4086,12 +4774,11 @@ sharkd_session_process_download(char *buf, const jsmntok_t *tokens, int count) const char *mime = "text/plain"; const char *filename = "keylog.txt"; - json_dumper_begin_object(&dumper); + sharkd_json_result_prologue(rpcid); sharkd_json_value_string("file", filename); sharkd_json_value_string("mime", mime); sharkd_json_value_base64("data", str, str_len); - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); } g_free(str); } @@ -4103,14 +4790,20 @@ sharkd_session_process_download(char *buf, const jsmntok_t *tokens, int count) memset(&rtp_req, 0, sizeof(rtp_req)); if (!sharkd_rtp_match_init(&rtp_req.id, tok_token + 4)) { - fprintf(stderr, "sharkd_session_process_download() rtp tokenizing error %s\n", tok_token); + sharkd_json_error( + rpcid, -10001, NULL, + "sharkd_session_process_download() rtp tokenizing error %s", tok_token + ); return; } tap_error = register_tap_listener("rtp", &rtp_req, NULL, 0, NULL, sharkd_session_packet_download_tap_rtp_cb, NULL, NULL); if (tap_error) { - fprintf(stderr, "sharkd_session_process_download() rtp error=%s", tap_error->str); + sharkd_json_error( + rpcid, -10002, NULL, + "sharkd_session_process_download() rtp error %s", tap_error->str + ); g_string_free(tap_error, TRUE); return; } @@ -4123,7 +4816,7 @@ sharkd_session_process_download(char *buf, const jsmntok_t *tokens, int count) const char *mime = "audio/x-wav"; const char *filename = tok_token; - json_dumper_begin_object(&dumper); + sharkd_json_result_prologue(rpcid); sharkd_json_value_string("file", filename); sharkd_json_value_string("mime", mime); @@ -4132,8 +4825,7 @@ sharkd_session_process_download(char *buf, const jsmntok_t *tokens, int count) sharkd_rtp_download_decode(&rtp_req); json_dumper_end_base64(&dumper); - json_dumper_end_object(&dumper); - json_dumper_finish(&dumper); + sharkd_json_result_epilogue(); g_slist_free_full(rtp_req.packets, sharkd_rtp_download_free_items); } @@ -4143,114 +4835,58 @@ sharkd_session_process_download(char *buf, const jsmntok_t *tokens, int count) static void sharkd_session_process(char *buf, const jsmntok_t *tokens, int count) { - int i; - - /* sanity check, and split strings */ - if (count < 1 || tokens[0].type != JSMN_OBJECT) + if (json_prep(buf, tokens, count)) { - fprintf(stderr, "sanity check(1): [0] not object\n"); - return; - } + /* don't need [0] token */ + tokens++; + count--; - /* don't need [0] token */ - tokens++; - count--; + const char* tok_method = json_find_attr(buf, tokens, count, "method"); - if (count & 1) - { - fprintf(stderr, "sanity check(2): %d not even\n", count); - return; - } - - for (i = 0; i < count; i += 2) - { - if (tokens[i].type != JSMN_STRING) - { - fprintf(stderr, "sanity check(3): [%d] not string\n", i); - return; - } - - if (tokens[i + 1].type != JSMN_STRING && tokens[i + 1].type != JSMN_PRIMITIVE) - { - fprintf(stderr, "sanity check(3a): [%d] wrong type\n", i + 1); - return; - } - - buf[tokens[i + 0].end] = '\0'; - buf[tokens[i + 1].end] = '\0'; - - /* unescape only value, as keys are simple strings */ - if (tokens[i + 1].type == JSMN_STRING && !json_decode_string_inplace(&buf[tokens[i + 1].start])) - { - fprintf(stderr, "sanity check(3b): [%d] cannot unescape string\n", i + 1); - return; - } - } - - { - const char *tok_req = json_find_attr(buf, tokens, count, "req"); - - if (!tok_req) - { - fprintf(stderr, "sanity check(4): no \"req\".\n"); - return; - } - - if (!strcmp(tok_req, "load")) + if (!strcmp(tok_method, "load")) sharkd_session_process_load(buf, tokens, count); - else if (!strcmp(tok_req, "status")) + else if (!strcmp(tok_method, "status")) sharkd_session_process_status(); - else if (!strcmp(tok_req, "analyse")) + else if (!strcmp(tok_method, "analyse")) sharkd_session_process_analyse(); - else if (!strcmp(tok_req, "info")) + else if (!strcmp(tok_method, "info")) sharkd_session_process_info(); - else if (!strcmp(tok_req, "check")) + else if (!strcmp(tok_method, "check")) sharkd_session_process_check(buf, tokens, count); - else if (!strcmp(tok_req, "complete")) + else if (!strcmp(tok_method, "complete")) sharkd_session_process_complete(buf, tokens, count); - else if (!strcmp(tok_req, "frames")) + else if (!strcmp(tok_method, "frames")) sharkd_session_process_frames(buf, tokens, count); - else if (!strcmp(tok_req, "tap")) + else if (!strcmp(tok_method, "tap")) sharkd_session_process_tap(buf, tokens, count); - else if (!strcmp(tok_req, "follow")) + else if (!strcmp(tok_method, "follow")) sharkd_session_process_follow(buf, tokens, count); - else if (!strcmp(tok_req, "iograph")) + else if (!strcmp(tok_method, "iograph")) sharkd_session_process_iograph(buf, tokens, count); - else if (!strcmp(tok_req, "intervals")) + else if (!strcmp(tok_method, "intervals")) sharkd_session_process_intervals(buf, tokens, count); - else if (!strcmp(tok_req, "frame")) + else if (!strcmp(tok_method, "frame")) sharkd_session_process_frame(buf, tokens, count); - else if (!strcmp(tok_req, "setcomment")) + else if (!strcmp(tok_method, "setcomment")) sharkd_session_process_setcomment(buf, tokens, count); - else if (!strcmp(tok_req, "setconf")) + else if (!strcmp(tok_method, "setconf")) sharkd_session_process_setconf(buf, tokens, count); - else if (!strcmp(tok_req, "dumpconf")) + else if (!strcmp(tok_method, "dumpconf")) sharkd_session_process_dumpconf(buf, tokens, count); - else if (!strcmp(tok_req, "download")) + else if (!strcmp(tok_method, "download")) sharkd_session_process_download(buf, tokens, count); - else if (!strcmp(tok_req, "bye")) + else if (!strcmp(tok_method, "bye")) + { + sharkd_json_simple_ok(rpcid); exit(0); + } else - fprintf(stderr, "::: req = %s\n", tok_req); - - /* reply for every command are 0+ lines of JSON reply (outputed above), finished by empty new line */ - json_dumper_finish(&dumper); - - /* - * We do an explicit fflush after every line, because - * we want output to be written to the socket as soon - * as the line is complete. - * - * The stream is fully-buffered by default, so it's - * only flushed when the buffer fills or the FILE * - * is closed. On UN*X, we could set it to be line - * buffered, but the MSVC standard I/O routines don't - * support line buffering - they only support *byte* - * buffering, doing a write for every byte written, - * which is too inefficient, and full buffering, - * which is what you get if you request line buffering. - */ - fflush(stdout); + { + sharkd_json_error( + rpcid, -32601, NULL, + "The method \"%s\" is unknown", tok_method + ); + } } } @@ -4282,8 +4918,11 @@ sharkd_session_main(int mode_setting) ret = json_parse(buf, NULL, 0); if (ret <= 0) { - fprintf(stderr, "invalid JSON -> closing\n"); - return 1; + sharkd_json_error( + rpcid, -32600, NULL, + "Invalid JSON(1)" + ); + continue; } /* fprintf(stderr, "JSON: %d tokens\n", ret); */ @@ -4300,8 +4939,11 @@ sharkd_session_main(int mode_setting) ret = json_parse(buf, tokens, ret); if (ret <= 0) { - fprintf(stderr, "invalid JSON(2) -> closing\n"); - return 2; + sharkd_json_error( + rpcid, -32600, NULL, + "Invalid JSON(2)" + ); + continue; } host_name_lookup_process(); diff --git a/test/suite_sharkd.py b/test/suite_sharkd.py index 0852d626f5..b5348cdaf2 100644 --- a/test/suite_sharkd.py +++ b/test/suite_sharkd.py @@ -63,45 +63,51 @@ def check_sharkd_session(run_sharkd_session, request): class case_sharkd(subprocesstest.SubprocessTestCase): def test_sharkd_req_load_bad_pcap(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('non-existant.pcap')}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('non-existant.pcap')} + }, ), ( - {"err": 2}, + {"jsonrpc":"2.0","id":1,"error":{"code":-2001,"message":"Unable to open the file"}}, )) def test_sharkd_req_status_no_pcap(self, check_sharkd_session): check_sharkd_session(( - {"req": "status"}, + {"jsonrpc":"2.0", "id":1, "method":"status"}, ), ( - {"frames": 0, "duration": 0.0}, + {"jsonrpc":"2.0","id":1,"result":{"frames":0,"duration":0.000000000}}, )) def test_sharkd_req_status(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "status"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"status"}, ), ( - {"err": 0}, - {"frames": 4, "duration": 0.070345000, - "filename": "dhcp.pcap", "filesize": 1400}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"result":{"frames": 4, "duration": 0.070345000, + "filename": "dhcp.pcap", "filesize": 1400}}, )) def test_sharkd_req_analyse(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "analyse"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"analyse"}, ), ( - {"err": 0}, - {"frames": 4, "protocols": ["frame", "eth", "ethertype", "ip", "udp", - "dhcp"], "first": 1102274184.317452908, "last": 1102274184.387798071}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"result":{"frames": 4, "protocols": ["frame", "eth", "ethertype", "ip", "udp", + "dhcp"], "first": 1102274184.317452908, "last": 1102274184.387798071}}, )) def test_sharkd_req_info(self, check_sharkd_session): matchTapNameList = MatchList( {"tap": MatchAny(str), "name": MatchAny(str)}) check_sharkd_session(( - {"req": "info"}, + {"jsonrpc":"2.0", "id":1, "method":"info"}, ), ( - { + {"jsonrpc":"2.0","id":1,"result":{ "version": MatchAny(str), "columns": MatchList({"format": MatchAny(str), "name": MatchAny(str)}), "stats": matchTapNameList, @@ -114,81 +120,95 @@ class case_sharkd(subprocesstest.SubprocessTestCase): "follow": matchTapNameList, "ftypes": MatchList(MatchAny(str)), "nstat": matchTapNameList, - }, + }}, )) def test_sharkd_req_check(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "check"}, - {"req": "check", "filter": "garbage filter"}, - {"req": "check", "field": "garbage field"}, - {"req": "check", "filter": "ip", "field": "ip"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"check"}, + {"jsonrpc":"2.0", "id":3, "method":"check", "params":{"filter": "garbage filter"}}, + {"jsonrpc":"2.0", "id":4, "method":"check", "params":{"field": "garbage field"}}, + {"jsonrpc":"2.0", "id":5, "method":"check", "params":{"filter": "ip", "field": "ip"}}, ), ( - {"err": 0}, - {"err": 0}, - {"err": 0, "filter": '"filter" was unexpected in this context.'}, - {"err": 0, "field": "notfound"}, - {"err": 0, "filter": "ok", "field": "ok"}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":3,"error":{"code":-5001,"message":"Filter invalid - \"filter\" was unexpected in this context."}}, + {"jsonrpc":"2.0","id":4,"error":{"code":-5002,"message":"Field garbage field not found"}}, + {"jsonrpc":"2.0","id":5,"result":{"status":"OK"}}, )) def test_sharkd_req_complete_field(self, check_sharkd_session): check_sharkd_session(( - {"req": "complete"}, - {"req": "complete", "field": "frame.le"}, - {"req": "complete", "field": "garbage.nothing.matches"}, + {"jsonrpc":"2.0", "id":1, "method":"complete"}, + {"jsonrpc":"2.0", "id":2, "method":"complete", "params":{"field": "frame.le"}}, + {"jsonrpc":"2.0", "id":3, "method":"complete", "params":{"field": "garbage.nothing.matches"}}, ), ( - {"err": 0}, - {"err": 0, "field": MatchList( - {"f": "frame.len", "t": 7, "n": "Frame length on the wire"}, match_element=any)}, - {"err": 0, "field": []}, + {"jsonrpc":"2.0","id":1,"result":{}}, + {"jsonrpc":"2.0","id":2,"result":{"field": MatchList( + {"f": "frame.len", "t": 7, "n": "Frame length on the wire"}, match_element=any)} + }, + {"jsonrpc":"2.0","id":3,"result":{"field": []}}, )) def test_sharkd_req_complete_pref(self, check_sharkd_session): check_sharkd_session(( - {"req": "complete", "pref": "tcp."}, - {"req": "complete", "pref": "garbage.nothing.matches"}, + {"jsonrpc":"2.0", "id":1, "method":"complete", "params":{"pref": "tcp."}}, + {"jsonrpc":"2.0", "id":2, "method":"complete", "params":{"pref": "garbage.nothing.matches"}}, ), ( - {"err": 0, "pref": MatchList( - {"f": "tcp.check_checksum", "d": "Validate the TCP checksum if possible"}, match_element=any)}, - {"err": 0, "pref": []}, + {"jsonrpc":"2.0","id":1,"result":{"pref": MatchList( + {"f": "tcp.check_checksum", "d": "Validate the TCP checksum if possible"}, match_element=any)} + }, + {"jsonrpc":"2.0","id":2,"result":{"pref": []}}, )) def test_sharkd_req_frames(self, check_sharkd_session, capture_file): # XXX need test for optional input parameters, ignored/marked/commented check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "frames"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"frames"}, ), ( - {"err": 0}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"result": MatchList({ "c": MatchList(MatchAny(str)), "num": MatchAny(int), "bg": MatchAny(str), "fg": MatchAny(str), - }), + }) + }, )) def test_sharkd_req_tap_invalid(self, check_sharkd_session, capture_file): # XXX Unrecognized taps result in an empty line, modify # run_sharkd_session such that checking for it is possible. check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "tap"}, - {"req": "tap", "tap0": "garbage tap"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"tap"}, + {"jsonrpc":"2.0", "id":3, "method":"tap", "params":{"tap0": "garbage tap"}}, ), ( - {"err": 0}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter tap0 is missing"}}, + {"jsonrpc":"2.0","id":3,"error":{"code":-11012,"message":"sharkd_session_process_tap() garbage tap not recognized"}}, )) def test_sharkd_req_tap(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "tap"}, - {"req": "tap", "tap0": "conv:Ethernet", "tap1": "endpt:TCP"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"tap"}, + {"jsonrpc":"2.0", "id":3, "method":"tap", "params":{"tap0": "conv:Ethernet", "tap1": "endpt:TCP"}}, ), ( - {"err": 0}, - { - "err": 0, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter tap0 is missing"}}, + {"jsonrpc":"2.0","id":3,"result":{ "taps": [ { "tap": "endpt:TCP", @@ -228,108 +248,160 @@ class case_sharkd(subprocesstest.SubprocessTestCase): ], }, ] - }, + }}, )) def test_sharkd_req_follow_bad(self, check_sharkd_session, capture_file): # Unrecognized taps currently produce no output (not even err). check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "follow"}, - {"req": "follow", "follow": "garbage follow", "filter": "ip"}, - {"req": "follow", "follow": "HTTP", "filter": "garbage filter"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"follow"}, + {"jsonrpc":"2.0", "id":3, "method":"follow", + "params":{"follow": "garbage follow", "filter": "ip"} + }, + {"jsonrpc":"2.0", "id":4, "method":"follow", + "params":{"follow": "HTTP", "filter": "garbage filter"} + }, ), ( - {"err": 0}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter follow is missing"}}, + {"jsonrpc":"2.0","id":3,"error":{"code":-12001,"message":"sharkd_session_process_follow() follower=garbage follow not found"}}, + {"jsonrpc":"2.0","id":4, + "error":{"code":-12002,"message":"sharkd_session_process_follow() name=HTTP error=Filter \"garbage filter\" is invalid - \"filter\" was unexpected in this context."} + }, )) def test_sharkd_req_follow_no_match(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "follow", "follow": "HTTP", "filter": "ip"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"follow", + "params":{"follow": "HTTP", "filter": "ip"} + }, ), ( - {"err": 0}, - {"err": 0, "shost": "NONE", "sport": "0", "sbytes": 0, - "chost": "NONE", "cport": "0", "cbytes": 0}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2, + "result":{"shost": "NONE", "sport": "0", "sbytes": 0, + "chost": "NONE", "cport": "0", "cbytes": 0} + }, )) def test_sharkd_req_follow_udp(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "follow", "follow": "UDP", "filter": "frame.number==1"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"follow", + "params":{"follow": "UDP", "filter": "frame.number==1"} + }, ), ( - {"err": 0}, - {"err": 0, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2, + "result":{ "shost": "255.255.255.255", "sport": "67", "sbytes": 272, "chost": "0.0.0.0", "cport": "68", "cbytes": 0, "payloads": [ - {"n": 1, "d": MatchRegExp(r'AQEGAAAAPR0A[a-zA-Z0-9]{330}AANwQBAwYq/wAAAAAAAAA=')}]}, + {"n": 1, "d": MatchRegExp(r'AQEGAAAAPR0A[a-zA-Z0-9]{330}AANwQBAwYq/wAAAAAAAAA=')}]} + }, )) def test_sharkd_req_iograph_bad(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "iograph"}, - {"req": "iograph", "graph0": "garbage graph name"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"iograph"}, + {"jsonrpc":"2.0", "id":3, "method":"iograph", + "params":{"graph0": "garbage graph name"} + }, ), ( - {"err": 0}, - {"iograph": []}, - {"iograph": []}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter graph0 is missing"}}, + {"jsonrpc":"2.0","id":3,"result":{"iograph": []}}, )) def test_sharkd_req_iograph_basic(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "iograph", "graph0": "max:udp.length", "filter0": "udp.length"}, - {"req": "iograph", "graph0": "packets", "graph1": "bytes"}, - {"req": "iograph", "graph0": "packets", "filter0": "garbage filter"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":1, "method":"iograph", + "params":{"graph0": "max:udp.length", "filter0": "udp.length"} + }, + {"jsonrpc":"2.0", "id":2, "method":"iograph", + "params":{"graph0": "packets", "graph1": "bytes"} + }, + {"jsonrpc":"2.0", "id":3, "method":"iograph", + "params":{"graph0": "packets", "filter0": "garbage filter"} + }, ), ( - {"err": 0}, - {"iograph": [{"items": [308.000000]}]}, - {"iograph": [{"items": [4.000000]}, {"items": [1312.000000]}]}, - {"iograph": [ - {"errmsg": 'Filter "garbage filter" is invalid - "filter" was unexpected in this context.'}]}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":1,"result":{"iograph": [{"items": [308.000000]}]}}, + {"jsonrpc":"2.0","id":2,"result":{"iograph": [{"items": [4.000000]}, {"items": [1312.000000]}]}}, + {"jsonrpc":"2.0","id":3,"error":{"code":-6001,"message":"Filter \"garbage filter\" is invalid - \"filter\" was unexpected in this context."}}, )) def test_sharkd_req_intervals_bad(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "intervals", "filter": "garbage filter"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"intervals", + "params":{"filter": "garbage filter"} + }, ), ( - {"err": 0}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"error":{"code":-7001,"message":"Invalid filter parameter: garbage filter"}}, )) def test_sharkd_req_intervals_basic(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "intervals"}, - {"req": "intervals", "interval": 1}, - {"req": "intervals", "filter": "frame.number <= 2"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"intervals"}, + {"jsonrpc":"2.0", "id":3, "method":"intervals", + "params":{"interval": 1} + }, + {"jsonrpc":"2.0", "id":4, "method":"intervals", + "params":{"filter": "frame.number <= 2"} + }, ), ( - {"err": 0}, - {"intervals": [[0, 4, 1312]], "last": 0, - "frames": 4, "bytes": 1312}, - {"intervals": [[0, 2, 656], [70, 2, 656]], - "last": 70, "frames": 4, "bytes": 1312}, - {"intervals": [[0, 2, 656]], "last": 0, "frames": 2, "bytes": 656}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"result":{"intervals":[[0,4,1312]],"last":0,"frames":4,"bytes":1312}}, + {"jsonrpc":"2.0","id":3,"result":{"intervals":[[0,2,656],[70,2,656]],"last":70,"frames":4,"bytes":1312}}, + {"jsonrpc":"2.0","id":4,"result":{"intervals":[[0,2,656]],"last":0,"frames":2,"bytes":656}}, )) def test_sharkd_req_frame_basic(self, check_sharkd_session, capture_file): # XXX add more tests for other options (ref_frame, prev_frame, columns, color, bytes, hidden) check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "frame", "frame": 2}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"frame", + "params":{"frame": 2} + }, ), ( - {"err": 0}, - {"err": 0, "fol": [["UDP", "udp.stream eq 1"]]}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"result":{"fol": [["UDP", "udp.stream eq 1"]]}}, )) def test_sharkd_req_frame_proto(self, check_sharkd_session, capture_file): # Check proto tree output (including an UTF-8 value). check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - {"req": "frame", "frame": 2, "proto": True}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"frame", + "params":{"frame": 2, "proto": True} + }, ), ( - {"err": 0}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"result": MatchObject({ "tree": MatchList({ "l": "Dynamic Host Configuration Protocol (Offer)", @@ -343,119 +415,160 @@ class case_sharkd(subprocesstest.SubprocessTestCase): }, match_element=any), # match one element from 'n' "h": [42, 300], }, match_element=any), # match one element from 'tree' - }), + }) + }, )) def test_sharkd_req_setcomment(self, check_sharkd_session, capture_file): check_sharkd_session(( - {"req": "load", "file": capture_file('dhcp.pcap')}, - # invalid frame number returns early. - {"req": "setcomment", "frame": 99999, "comment": "meh\nbaz"}, - {"req": "setcomment", "frame": 3, "comment": "foo\nbar"}, - {"req": "frame", "frame": 3}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('dhcp.pcap')} + }, + {"jsonrpc":"2.0", "id":2, "method":"setcomment", + "params":{"frame": 99999, "comment": "meh\nbaz"} + }, + {"jsonrpc":"2.0", "id":3, "method":"setcomment", + "params":{"frame": 3, "comment": "foo\nbar"} + }, + {"jsonrpc":"2.0", "id":4, "method":"frame", + "params":{"frame": 3} + }, ), ( - {"err": 0}, - {"err": 0}, - {"err": 0, "comment": "foo\nbar", "fol": MatchAny(list)}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"error":{"code":-3002,"message":"Frame number is out of range"}}, + {"jsonrpc":"2.0","id":3,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":4,"result":{"comment":"foo\nbar","fol": MatchAny(list)}}, )) def test_sharkd_req_setconf_bad(self, check_sharkd_session): check_sharkd_session(( - {"req": "setconf", "name": "uat:garbage-pref", "value": "\"\""}, + {"jsonrpc":"2.0", "id":1, "method":"setconf", + "params":{"name": "uat:garbage-pref", "value": "\"\""} + }, ), ( - {"err": 1, "errmsg": "Unknown preference"}, + {"jsonrpc":"2.0","id":1,"error":{"code":-4005,"message":"Unable to set the preference"}}, )) def test_sharkd_req_dumpconf_bad(self, check_sharkd_session): check_sharkd_session(( - {"req": "dumpconf", "pref": "invalid-garbage-preference"}, - {"req": "dumpconf", "pref": "uat:custom_http_header_fields"}, - ), ()) + {"jsonrpc":"2.0", "id":1, "method":"dumpconf", + "params":{"pref": "bad.preference"} + }, + {"jsonrpc":"2.0", "id":2, "method":"dumpconf", + "params":{"pref": "invalid-garbage-preference"} + }, + {"jsonrpc":"2.0", "id":3, "method":"dumpconf", + "params":{"pref": "uat:custom_http_header_fields"} + }, + ), ( + {"jsonrpc":"2.0","id":1,"error":{"code":-9001,"message":"Invalid pref bad.preference."}}, + {"jsonrpc":"2.0","id":2,"error":{"code":-9002,"message":"Invalid pref invalid-garbage-preference."}}, + {"jsonrpc":"2.0","id":3,"error":{"code":-9002,"message":"Invalid pref uat:custom_http_header_fields."}}, + )) def test_sharkd_req_dumpconf_all(self, check_sharkd_session): check_sharkd_session(( - {"req": "dumpconf"}, + {"jsonrpc":"2.0", "id":1, "method":"dumpconf"}, ), ( - {"prefs": MatchObject({"tcp.check_checksum": {"b": 0}})}, + {"jsonrpc":"2.0","id":1,"result":{"prefs": MatchObject({"tcp.check_checksum": {"b": 0}})} + }, )) def test_sharkd_req_download_tls_secrets(self, check_sharkd_session, capture_file): # XXX test download for eo: and rtp: too check_sharkd_session(( - {"req": "load", "file": capture_file('tls12-dsb.pcapng')}, - {"req": "download", "token": "ssl-secrets"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file('tls12-dsb.pcapng')} + }, + {"jsonrpc":"2.0", "id":2, "method":"download", + "params":{"token": "ssl-secrets"} + }, ), ( - {"err": 0}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, # TODO remove "RSA Session-ID:" support and support "CLIENT_RANDOM "... only - {"file": "keylog.txt", "mime": "text/plain", - "data": MatchRegExp(r'UlNBIFNlc3Npb24tSUQ6.+')}, + {"jsonrpc":"2.0","id":2,"result":{"file": "keylog.txt", "mime": "text/plain", + "data": MatchRegExp(r'UlNBIFNlc3Npb24tSUQ6.+')} + }, )) def test_sharkd_req_bye(self, check_sharkd_session): check_sharkd_session(( - {"req": "bye"}, + {"jsonrpc":"2.0", "id":1, "method":"bye"}, ), ( + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, )) def test_sharkd_bad_request(self, check_sharkd_session): check_sharkd_session(( - {"req": 1337}, + {"jsonrpc":"2.0", "id":1, "method":"dud"}, ), ( + {'jsonrpc': '2.0', 'id': 1, 'error': {'code': -32601, 'message': 'The method dud is not supported'}}, )) def test_sharkd_config(self, check_sharkd_session): check_sharkd_session(( - {"req": "setconf", "name": "uat:custom_http_header_fields", - "value": "\"X-Header-Name\", \"Description\""}, - {"req": "setconf", "name": "tcp.check_checksum", "value": "TRUE"}, - {"req": "dumpconf", "pref": "tcp.check_checksum"}, - {"req": "setconf", "name": "tcp.check_checksum", "value": "FALSE"}, - {"req": "dumpconf", "pref": "tcp.check_checksum"}, + {"jsonrpc":"2.0", "id":1, "method":"setconf", + "params":{"name": "uat:custom_http_header_fields", "value": "\"X-Header-Name\", \"Description\""} + }, + {"jsonrpc":"2.0", "id":2, "method":"setconf", + "params":{"name": "tcp.check_checksum", "value": "true"} + }, + {"jsonrpc":"2.0", "id":3, "method":"dumpconf", + "params":{"pref": "tcp.check_checksum"} + }, + {"jsonrpc":"2.0", "id":4, "method":"setconf", + "params":{"name": "tcp.check_checksum", "value": "false"} + }, + {"jsonrpc":"2.0", "id":5, "method":"dumpconf", + "params":{"pref": "tcp.check_checksum"} + }, ), ( # Check that the UAT preference is set. There is no way to query it # (other than testing for side-effects in dissection). - {"err": 0}, - {"err": 0}, - {"prefs": {"tcp.check_checksum": {"b": 1}}}, - {"err": 0}, - {"prefs": {"tcp.check_checksum": {"b": 0}}}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":2,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":3,"result":{"prefs":{"tcp.check_checksum":{"b":1}}}}, + {"jsonrpc":"2.0","id":4,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":5,"result":{"prefs":{"tcp.check_checksum":{"b":0}}}}, )) def test_sharkd_config_enum(self, check_sharkd_session): '''Dump default enum preference value, change it and restore it.''' check_sharkd_session(( - {"req": "dumpconf", "pref": "wlan.ignore_wep"}, - {"req": "setconf", "name": "wlan.ignore_wep", "value": "Yes - with IV"}, - {"req": "dumpconf", "pref": "wlan.ignore_wep"}, - {"req": "setconf", "name": "wlan.ignore_wep", "value": "No"}, - {"req": "dumpconf", "pref": "wlan.ignore_wep"}, + {"jsonrpc":"2.0", "id":1, "method":"dumpconf", + "params":{"pref": "wlan.ignore_wep"} + }, + {"jsonrpc":"2.0", "id":2, "method":"setconf", + "params":{"name": "wlan.ignore_wep", "value": "Yes - with IV"} + }, + {"jsonrpc":"2.0", "id":3, "method":"dumpconf", + "params":{"pref": "wlan.ignore_wep"} + }, + {"jsonrpc":"2.0", "id":4, "method":"setconf", + "params":{"name": "wlan.ignore_wep", "value": "No"} + }, + {"jsonrpc":"2.0", "id":5, "method":"dumpconf", + "params":{"pref": "wlan.ignore_wep"} + }, ), ( - {"prefs": {"wlan.ignore_wep": {"e": [ - {"v": 0, "s": 1, "d": "No"}, - {"v": 1, "d": "Yes - without IV"}, - {"v": 2, "d": "Yes - with IV"} - ]}}}, - {"err": 0}, - {"prefs": {"wlan.ignore_wep": {"e": [ - {"v": 0, "d": "No"}, - {"v": 1, "d": "Yes - without IV"}, - {"v": 2, "s": 1, "d": "Yes - with IV"} - ]}}}, - {"err": 0}, - {"prefs": {"wlan.ignore_wep": {"e": [ - {"v": 0, "s": 1, "d": "No"}, - {"v": 1, "d": "Yes - without IV"}, - {"v": 2, "d": "Yes - with IV"} - ]}}}, + {"jsonrpc":"2.0","id":1,"result":{"prefs":{"wlan.ignore_wep":{"e":[{"v":0,"s":1,"d":"No"},{"v":1,"d":"Yes - without IV"},{"v":2,"d":"Yes - with IV"}]}}}}, + {"jsonrpc":"2.0","id":2,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":3,"result":{"prefs":{"wlan.ignore_wep":{"e":[{"v":0,"d":"No"},{"v":1,"d":"Yes - without IV"},{"v":2,"s":1,"d":"Yes - with IV"}]}}}}, + {"jsonrpc":"2.0","id":4,"result":{"status":"OK"}}, + {"jsonrpc":"2.0","id":5,"result":{"prefs":{"wlan.ignore_wep":{"e":[{"v":0,"s":1,"d":"No"},{"v":1,"d":"Yes - without IV"},{"v":2,"d":"Yes - with IV"}]}}}}, )) def test_sharkd_nested_file(self, check_sharkd_session, capture_file): '''Request a frame from a file with a deep level of nesting.''' check_sharkd_session(( - {"req": "load", "file": capture_file("http2-data-reassembly.pcap")}, - {"req": "frame", "frame": "4", "proto": "yes"}, + {"jsonrpc":"2.0", "id":1, "method":"load", + "params":{"file": capture_file("http2-data-reassembly.pcap")} + }, + {"jsonrpc":"2.0", "id":2, "method":"frame", + "params":{"frame": "4", "proto": "yes"} + }, ), ( - {"err": 0}, + {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}}, MatchAny(), ))