From 0f4dff3144111e63ef0a04d114cdd847c43b82f4 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Fri, 16 May 2014 14:33:08 +1000 Subject: [PATCH] Parse a proxy subscribe response as flexibly as possible --- src/generator.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 3 deletions(-) diff --git a/src/generator.c b/src/generator.c index d3ba1448..2fb2ab1b 100644 --- a/src/generator.c +++ b/src/generator.c @@ -27,6 +27,7 @@ struct proxy_instance { char *enonce1; char *enonce1bin; char *sessionid; + int nonce2len; tv_t last_message; @@ -196,8 +197,83 @@ static bool connect_proxy(connsock_t *cs) return true; } +/* Decode a string that should have a json message and return just the contents + * of the result key or NULL. */ +static json_t *json_result(json_t *val) +{ + json_t *res_val = NULL, *err_val; + + res_val = json_object_get(val, "result"); + if (json_is_null(res_val)) + res_val = NULL; + if (!res_val) { + char *ss; + + err_val = json_object_get(val, "error"); + if (err_val) + ss = json_dumps(err_val, 0); + else + ss = strdup("(unknown reason)"); + + LOGWARNING("JSON-RPC decode failed: %s", ss); + free(ss); + } + return res_val; +} + +/* Parse a string and return the json value it contains, if any, and the + * result in res_val. Return NULL if no result key is found. */ +static json_t *json_msg_result(char *msg, json_t **res_val) +{ + json_error_t err; + json_t *val; + + *res_val = NULL; + val = json_loads(msg, 0, &err); + if (!val) { + LOGWARNING("Json decode failed(%d): %s", err.line, err.text); + goto out; + } + *res_val = json_result(val); + if (!*res_val) { + LOGWARNING("No json result found"); + json_decref(val); + val = NULL; + } + +out: + return val; +} + +/* For some reason notify is buried at various different array depths so use + * a reentrant function to try and find it. */ +static json_t *find_notify(json_t *val) +{ + int arr_size, i; + json_t *ret; + const char *entry; + + if (!json_is_array(val)) + return NULL; + arr_size = json_array_size(val); + entry = json_string_value(json_array_get(val, 0)); + if (entry && !strncasecmp(entry, "mining.notify", 13)) + return val; + for (i = 0; i < arr_size; i++) { + json_t *arr_val; + + arr_val = json_array_get(val, i); + ret = find_notify(arr_val); + if (ret) + break; + } + return ret; +} + static bool parse_subscribe(connsock_t *cs, proxy_instance_t *proxi) { + json_t *val = NULL, *res_val, *notify_val, *tmp; + const char *string; bool ret = false; int size; @@ -206,10 +282,63 @@ static bool parse_subscribe(connsock_t *cs, proxy_instance_t *proxi) LOGWARNING("Failed to receive line in parse_subscribe"); goto out; } - LOGWARNING("Got message: %s", cs->buf); - ret = true; + val = json_msg_result(cs->buf, &res_val); + if (!val) { + LOGWARNING("Failed to get a json result in parse_subscribe, got: %s", cs->buf); + goto out; + } + if (!json_is_array(res_val)) { + LOGWARNING("Result in parse_subscribe not an array"); + goto out; + } + size = json_array_size(res_val); + if (size < 3) { + LOGWARNING("Result in parse_subscribe array too small"); + goto out; + } + notify_val = find_notify(res_val); + if (!notify_val) { + LOGWARNING("Failed to find notify in parse_subscribe"); + goto out; + } + if (!proxi->no_params && !proxi->no_sessionid && json_array_size(notify_val) > 1) { + /* Copy the session id if one exists. */ + string = json_string_value(json_array_get(notify_val, 1)); + if (string) + proxi->sessionid = strdup(string); + } + tmp = json_array_get(res_val, 1); + if (!tmp || !json_is_string(tmp)) { + LOGWARNING("Failed to parse enonce1 in parse_subscribe"); + goto out; + } + string = json_string_value(tmp); + if (strlen(string) < 1) { + LOGWARNING("Invalid string length for enonce1 in parse_subscribe"); + goto out; + } + proxi->enonce1 = strdup(string); + size = strlen(proxi->enonce1) / 2; + proxi->enonce1bin = ckalloc(size); + hex2bin(proxi->enonce1bin, proxi->enonce1, size); + tmp = json_array_get(res_val, 2); + if (!tmp || !json_is_integer(tmp)) { + LOGWARNING("Failed to parse nonce2len in parse_subscribe"); + goto out; + } + size = json_integer_value(tmp); + if (size < 1 || size > 8) { + LOGWARNING("Invalid nonce2len %d in parse_subscribe", size); + goto out; + } + proxi->nonce2len = size; + + LOGWARNING("Found notify with enonce %s nonce2len %d !", proxi->enonce1, + proxi->nonce2len); out: + if (val) + json_decref(val); return ret; } @@ -303,7 +432,7 @@ static int proxy_mode(ckpool_t *ckp, proc_instance_t *pi, connsock_t *cs, const char *auth, const char *pass) { proxy_instance_t proxi; - int ret; + int ret = 1; memset(&proxi, 0, sizeof(proxi)); if (!connect_proxy(cs)) {