From a24bf3dd3cf3bd937b62d3a470a007af796a17a4 Mon Sep 17 00:00:00 2001 From: Tanguy Pruvot Date: Thu, 4 Feb 2016 04:10:55 +0100 Subject: [PATCH] stratum: allow to use curl (optional) for HTTP/1.1 chunks Todo: SSL RPC connections... user/pw vars may be required... Also, update the make files and handle better the getwork client requests.. Signed-off-by: Tanguy Pruvot --- stratum/Makefile | 14 +- stratum/algos/makefile | 7 +- stratum/client.cpp | 17 +- stratum/coind.cpp | 14 +- stratum/coind_template.cpp | 46 +++- stratum/json.cpp | 23 ++ stratum/json.h | 12 + stratum/rpc.cpp | 84 ++----- stratum/rpc.h | 2 + stratum/rpc_curl.cpp | 484 +++++++++++++++++++++++++++++++++++++ stratum/sha3/makefile | 9 +- stratum/socket.cpp | 24 +- stratum/stratum.h | 4 +- stratum/util.h | 8 +- 14 files changed, 642 insertions(+), 106 deletions(-) create mode 100644 stratum/rpc_curl.cpp diff --git a/stratum/Makefile b/stratum/Makefile index 5c343dc..8f12dd3 100755 --- a/stratum/Makefile +++ b/stratum/Makefile @@ -1,7 +1,8 @@ CC=gcc -CFLAGS=-c -g `mysql_config --cflags --libs` -march=native +CFLAGS= -g -march=native +SQLFLAGS= `mysql_config --cflags --libs` # Comment this line to disable address check on login, # if you use the auto exchange feature... @@ -24,6 +25,11 @@ SOURCES=stratum.cpp db.cpp coind.cpp coind_aux.cpp coind_template.cpp coind_subm client.cpp client_submit.cpp client_core.cpp client_difficulty.cpp remote.cpp remote_template.cpp \ user.cpp object.cpp json.cpp base58.cpp +CFLAGS += -DHAVE_CURL +SOURCES += rpc_curl.cpp +LDCURL = $(shell /usr/bin/pkg-config --static --libs libcurl) +LDFLAGS += $(LDCURL) + OBJECTS=$(SOURCES:.cpp=.o) OUTPUT=stratum @@ -43,13 +49,13 @@ projectcode2: $(SOURCES): stratum.h util.h $(OUTPUT): $(OBJECTS) - $(CC) $(LDFLAGS) $(OBJECTS) $(LDLIBS) -o $@ + $(CC) $(OBJECTS) $(LDLIBS) $(LDFLAGS) -o $@ .cpp.o: - $(CC) $(CFLAGS) $< + $(CC) $(CFLAGS) $(SQLFLAGS) -c $< .c.o: - $(CC) $(CFLAGS) $< + $(CC) $(CFLAGS) -c $< clean: rm -f *.o diff --git a/stratum/algos/makefile b/stratum/algos/makefile index e1619a7..1defd06 100644 --- a/stratum/algos/makefile +++ b/stratum/algos/makefile @@ -4,7 +4,8 @@ CC=gcc #CFLAGS=-c -g -I /usr/include/mysql #LDFLAGS=-g -CFLAGS=-c -std=gnu99 -O2 -I.. -I/usr/include/mysql -march=native +CXXFLAGS = -O2 -I.. -march=native +CFLAGS= $(CXXFLAGS) -std=gnu99 LDFLAGS=-O2 -lgmp SOURCES=lyra2re.c lyra2v2.c Lyra2.c Sponge.c blake.c scrypt.c c11.c x11.c x13.c sha256.c keccak.c \ @@ -25,10 +26,10 @@ $(OUTPUT): $(OBJECTS) ar rc $@ $(OBJECTS) .cpp.o: - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CXXFLAGS) -c $< -o $@ .c.o: - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) -c $< -o $@ # $(CC) $(CFLAGS) -std=gnu99 -Wno-pointer-sign -Wno-pointer-to-int-cast -funroll-loops -fvariable-expansion-in-unroller -fmerge-all-constants -fbranch-target-load-optimize2 -fsched2-use-superblocks -falign-loops=16 -falign-functions=16 -falign-jumps=16 -falign-labels=16 -Ofast -flto -fuse-linker-plugin -ftree-loop-if-convert-stores -DUSE_ASM -pg $< diff --git a/stratum/client.cpp b/stratum/client.cpp index 49dd4ee..bca2b74 100644 --- a/stratum/client.cpp +++ b/stratum/client.cpp @@ -173,11 +173,17 @@ bool client_authorize(YAAMP_CLIENT *client, json_value *json_params) { strncpy(client->username, json_params->u.array.values[0]->u.string.ptr, 1023); - client->username[34] = 0; - strncpy(client->worker, client->username+35, 1023); - -// debuglog("%s\n", client->username); -// debuglog("%s\n", client->worker); + char sep = client->username[34]; + if (sep == '.' || sep == ',' || sep == ':') { + client->username[34] = 0; + strncpy(client->worker, client->username+35, 1023-35); +// debuglog("%s\n", client->username); +// debuglog("%s\n", client->worker); + } else if (strlen(client->username) > 35) { + client->username[35] = 0; + strncpy(client->worker, client->username+36, 1023-36); + debuglog("address %s was too long, truncated to 35 chars\n", client->username); + } } bool reset = client_initialize_multialgo(client); @@ -225,7 +231,6 @@ bool client_authorize(YAAMP_CLIENT *client, json_value *json_params) if(client->jobid_locked) job_send_jobid(client, client->jobid_locked); - else job_send_last(client); diff --git a/stratum/coind.cpp b/stratum/coind.cpp index 4bf04a2..435791c 100644 --- a/stratum/coind.cpp +++ b/stratum/coind.cpp @@ -144,16 +144,20 @@ bool coind_validate_address(YAAMP_COIND *coind) void coind_init(YAAMP_COIND *coind) { + char params[YAAMP_SMALLBUFSIZE]; + char account[YAAMP_SMALLBUFSIZE] = { 0 }; + yaamp_create_mutex(&coind->mutex); - char account[YAAMP_SMALLBUFSIZE] = { 0 }; + coind->rpc.curl = 0; + if(!strcmp(coind->symbol, "DCR") || !strcmp(coind->symbol, "DCRD")) { + coind->rpc.curl = 1; + sprintf(account, "default"); + } + bool valid = coind_validate_address(coind); if(valid) return; - if(!strcmp(coind->symbol, "DCRD")) - sprintf(account, "default"); - - char params[YAAMP_SMALLBUFSIZE]; sprintf(params, "[\"%s\"]", account); json_value *json = rpc_call(&coind->rpc, "getaccountaddress", params); diff --git a/stratum/coind_template.cpp b/stratum/coind_template.cpp index 6b0dc20..3c24602 100644 --- a/stratum/coind_template.cpp +++ b/stratum/coind_template.cpp @@ -95,6 +95,44 @@ YAAMP_JOB_TEMPLATE *coind_create_template_memorypool(YAAMP_COIND *coind) //////////////////////////////////////////////////////////////////////////////////////////////////////////// +static int coind_parse_decred_header(YAAMP_JOB_TEMPLATE *templ, json_value *json) +{ + struct __attribute__((__packed__)) { + uint32_t version; + char prevblock[32]; + char merkleroot[32]; + char stakeroot[32]; + uint16_t votebits; + char finalstate[6]; + uint16_t voters; + uint8_t freshstake; + uint8_t revoc; + uint32_t poolsize; + uint32_t nbits; + uint64_t sbits; + uint32_t height; + uint32_t size; + uint32_t ntime; + uint32_t nonce; + char extra[36]; + } header; + + const char *header_hex = json_get_string(json, "header"); + if (!header_hex) return -1; + + //debuglog("HEADER: %s\n", header_hex); + + binlify((unsigned char*) &header, header_hex); + + templ->height = header.height; + sprintf(templ->version, "%08x", header.version); + sprintf(templ->ntime, "%08x", header.ntime); + sprintf(templ->nbits, "%08x", header.nbits); + hexlify(templ->prevhash_hex, (const unsigned char*) header.prevblock, 32); + + return 0; +} + YAAMP_JOB_TEMPLATE *coind_create_template(YAAMP_COIND *coind) { if(coind->usememorypool) @@ -150,9 +188,13 @@ YAAMP_JOB_TEMPLATE *coind_create_template(YAAMP_COIND *coind) const char *flags = json_get_string(json_coinbaseaux, "flags"); strcpy(templ->flags, flags ? flags : ""); + if (!strcmp(coind->symbol, "DCR") || !strcmp(coind->symbol, "DCRD")) { + coind_parse_decred_header(templ, json_result); + } + else if (!coind->height || !flags || !prev || !bits) { - stratumlog("%s incompatible getblocktemplate format : height=%d value=%d bits=%s prev=%s\n", - coind->symbol, coind->height, templ->value, bits, prev); + stratumlog("%s warning, gbt incorrect : version=%s height=%d value=%d bits=%s prev=%s\n", + coind->symbol, templ->version, templ->height, templ->value, templ->nbits, templ->prevhash_hex); } // temporary hack, until wallet is fixed... diff --git a/stratum/json.cpp b/stratum/json.cpp index 6f3bb2a..3cc1425 100644 --- a/stratum/json.cpp +++ b/stratum/json.cpp @@ -978,3 +978,26 @@ void json_value_free (json_value * value) json_value_free_ex (&settings, value); } +char* json_dumps(json_value * value, int opt) +{ + return strdup(""); // unsupported +} + +int json_integer_value(const json_value *json) +{ + json_int_t n; + if(!json_is_integer(json)) + return 0; + + n = *(json); + + return (int) n; +} + +char* json_string_value(const json_value *json) +{ + if(!json_is_string(json)) + return 0; + + return json->u.string.ptr; +} diff --git a/stratum/json.h b/stratum/json.h index 8d87bf2..922c75c 100644 --- a/stratum/json.h +++ b/stratum/json.h @@ -261,6 +261,18 @@ void json_value_free (json_value *); void json_value_free_ex (json_settings * settings, json_value *); +// todo +char* json_dumps(json_value * value, int opt); + +typedef json_value json_t; +#define json_typeof(json) ((json)->type) +#define json_is_array(json) (json && json_typeof(json) == json_array) +#define json_is_integer(json) (json && json_typeof(json) == json_integer) +#define json_is_string(json) (json && json_typeof(json) == json_string) +#define json_is_null(json) (json && json_typeof(json) == json_null) + +int json_integer_value(const json_value *json); +char* json_string_value(const json_value *json); #ifdef __cplusplus } /* extern "C" */ diff --git a/stratum/rpc.cpp b/stratum/rpc.cpp index a074f73..f81dce0 100644 --- a/stratum/rpc.cpp +++ b/stratum/rpc.cpp @@ -113,65 +113,14 @@ int rpc_send(YAAMP_RPC *rpc, const char *format, ...) return bytes; } -// Attempt to read decred HTTP 1.1 chunked data... not finished -char *rpc_get_chunks(YAAMP_RPC *rpc, char *buffer) -{ - char *val, *databuf = NULL; - int datalen, reslen = 0; - int respos = 0; - - header_value(buffer, "Connection:", val); - bool close = (strcmp(val, "close") == 0); - - const char *p = strstr(buffer, "\r\n\r\n"); - // read chunk len - if (p && sscanf(p+4, "%x", &datalen)) - { - p += 4; - reslen += datalen; - databuf = (char *)realloc(databuf, reslen + 4); - if(!databuf) { - debuglog("ERR: %s OOM", __func__); - goto done; - } - - p = strstr(p, "\r\n"); - if (p) p += 2; - while (datalen && p) { - memcpy(databuf + respos, p, datalen); - databuf[respos + datalen]='\0'; - respos += reslen; - p = strstr(p+datalen, "\r\n"); - if (!p || !sscanf(p+2, "%x", &datalen)) break; - //debuglog("chunk: len %d\n", datalen); - if (!datalen) break; - - reslen += datalen; - databuf = (char *)realloc(databuf, reslen + 4); - int bytes = recv(rpc->sock, buffer, min(YAAMP_SMALLBUFSIZE, datalen), 0); - if (bytes <=0) break; - p = buffer; - } - } - -done: - CommonUnlock(&rpc->mutex); - - if (close) { - rpc_connect(rpc); - } - - return databuf; -} - ///////////////////////////////////////////////////////////////////////////////// -char *rpc_do_call(YAAMP_RPC *rpc, char const *data, int http_ver) +char *rpc_do_call(YAAMP_RPC *rpc, char const *data) { CommonLock(&rpc->mutex); // HTTP 1.1 accepts chunked data, and keep the connection - rpc_send(rpc, "POST / HTTP/1.%d\r\n", http_ver); + rpc_send(rpc, "POST / HTTP/1.1\r\n"); rpc_send(rpc, "Authorization: Basic %s\r\n", rpc->credential); rpc_send(rpc, "Host: %s:%d\n", rpc->host, rpc->port); rpc_send(rpc, "Accept: */*\r\n"); @@ -230,25 +179,23 @@ char *rpc_do_call(YAAMP_RPC *rpc, char const *data, int http_ver) char tmp[1024]; header_value(buffer, "Transfer-Encoding:", tmp); - if (!strcmp(tmp, "chunked") && http_ver == 1) { - //return rpc_get_chunks(rpc, buffer); + if (!strcmp(tmp, "chunked")) { +#ifdef HAVE_CURL + if (!rpc->curl) debuglog("%s chunked transfer detected, switching to curl!\n", + rpc->coind->symbol); + rpc->curl = 1; +#endif CommonUnlock(&rpc->mutex); rpc_connect(rpc); - return rpc_do_call(rpc, data, 0); + return NULL; } int datalen = atoi(header_value(buffer, "Content-Length:", tmp)); if(!datalen) { - if (http_ver == 0) { - const char *end = strstr(buffer, "\r\n\r\n"); - if (end) datalen = strlen(end + 4); - } - if (!datalen) { - debuglog("ERROR: rpc No Content-Length header!\n"); - CommonUnlock(&rpc->mutex); - return NULL; - } + debuglog("ERROR: rpc No Content-Length header!\n"); + CommonUnlock(&rpc->mutex); + return NULL; } p = strstr(buffer, "\r\n\r\n"); @@ -296,6 +243,11 @@ json_value *rpc_call(YAAMP_RPC *rpc, char const *method, char const *params) { // debuglog("rpc_call :%d %s\n", rpc->port, method); +#ifdef HAVE_CURL + if (rpc->ssl || rpc->curl) + return rpc_curl_call(rpc, method, params); +#endif + int s1 = current_timestamp(); if(!rpc_connected(rpc)) return NULL; @@ -309,7 +261,7 @@ json_value *rpc_call(YAAMP_RPC *rpc, char const *method, char const *params) else sprintf(message, "{\"method\":\"%s\",\"id\":\"%d\"}", method, ++rpc->id); - char *buffer = rpc_do_call(rpc, message, 1); + char *buffer = rpc_do_call(rpc, message); free(message); if(!buffer) return NULL; diff --git a/stratum/rpc.h b/stratum/rpc.h index 53f7bf8..546cdf5 100644 --- a/stratum/rpc.h +++ b/stratum/rpc.h @@ -11,6 +11,7 @@ struct YAAMP_RPC char cert[1024]; int ssl; + int curl; int sock; int id; @@ -32,3 +33,4 @@ int rpc_flush(YAAMP_RPC *rpc); json_value *rpc_call(YAAMP_RPC *rpc, char const *method, char const *params=NULL); +json_value *rpc_curl_call(YAAMP_RPC *rpc, char const *method, char const *params); diff --git a/stratum/rpc_curl.cpp b/stratum/rpc_curl.cpp new file mode 100644 index 0000000..a40cfd6 --- /dev/null +++ b/stratum/rpc_curl.cpp @@ -0,0 +1,484 @@ + +#include "stratum.h" +#include + +//#define RPC_DEBUGLOG_ + +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#include +#endif + +static __thread CURL *curl = NULL; + +bool opt_timeout = CURL_RPC_TIMEOUT; // 30sec +bool opt_debug = false; +bool opt_protocol = false; +bool opt_proxy = false; +long opt_proxy_type = 0; //CURLPROXY_SOCKS5; + +#define USER_AGENT "stratum/yiimp" +#define JSON_INDENT(x) 0 +#define json_object_get(j,k) json_get_object(j,k) + +struct data_buffer { + void *buf; + size_t len; +}; + +struct upload_buffer { + const void *buf; + size_t len; + size_t pos; +}; + +struct header_info { + char *lp_path; + char *reason; + char *stratum_url; +}; + +static void databuf_free(struct data_buffer *db) +{ + if (!db) + return; + + free(db->buf); + + memset(db, 0, sizeof(*db)); +} + +static size_t all_data_cb(const void *ptr, size_t size, size_t nmemb, + void *user_data) +{ + struct data_buffer *db = (struct data_buffer *)user_data; + size_t len = size * nmemb; + size_t oldlen, newlen; + void *newmem; + static const unsigned char zero = 0; + + oldlen = db->len; + newlen = oldlen + len; + + newmem = realloc(db->buf, newlen + 1); + if (!newmem) + return 0; + + db->buf = newmem; + db->len = newlen; + memcpy((char*)db->buf + oldlen, ptr, len); + memcpy((char*)db->buf + newlen, &zero, 1); /* null terminate */ + + return len; +} + +static size_t upload_data_cb(void *ptr, size_t size, size_t nmemb, + void *user_data) +{ + struct upload_buffer *ub = (struct upload_buffer *)user_data; + unsigned int len = (unsigned int)(size * nmemb); + + if (len > ub->len - ub->pos) + len = (unsigned int)(ub->len - ub->pos); + + if (len) { + memcpy(ptr, (char*)ub->buf + ub->pos, len); + ub->pos += len; + } + + return len; +} + +#if LIBCURL_VERSION_NUM >= 0x071200 +static int seek_data_cb(void *user_data, curl_off_t offset, int origin) +{ + struct upload_buffer *ub = (struct upload_buffer *)user_data; + + switch (origin) { + case SEEK_SET: + ub->pos = (size_t)offset; + break; + case SEEK_CUR: + ub->pos += (size_t)offset; + break; + case SEEK_END: + ub->pos = ub->len + (size_t)offset; + break; + default: + return 1; /* CURL_SEEKFUNC_FAIL */ + } + + return 0; /* CURL_SEEKFUNC_OK */ +} +#endif + +static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data) +{ + struct header_info *hi = (struct header_info *)user_data; + size_t remlen, slen, ptrlen = size * nmemb; + char *rem, *val = NULL, *key = NULL; + void *tmp; + + val = (char*)calloc(1, ptrlen); + key = (char*)calloc(1, ptrlen); + if (!key || !val) + goto out; + + tmp = memchr(ptr, ':', ptrlen); + if (!tmp || (tmp == ptr)) /* skip empty keys / blanks */ + goto out; + slen = (size_t)((char*)tmp - (char*)ptr); + if ((slen + 1) == ptrlen) /* skip key w/ no value */ + goto out; + memcpy(key, ptr, slen); /* store & nul term key */ + key[slen] = 0; + + rem = (char*)ptr + slen + 1; /* trim value's leading whitespace */ + remlen = ptrlen - slen - 1; + while ((remlen > 0) && (isspace(*rem))) { + remlen--; + rem++; + } + + memcpy(val, rem, remlen); /* store value, trim trailing ws */ + val[remlen] = 0; + while ((*val) && (isspace(val[strlen(val) - 1]))) { + val[strlen(val) - 1] = 0; + } + +out: + free(key); + free(val); + return ptrlen; +} + +#if LIBCURL_VERSION_NUM >= 0x070f06 +static int sockopt_keepalive_cb(void *userdata, curl_socket_t fd, + curlsocktype purpose) +{ + int keepalive = 1; + int tcp_keepcnt = 3; + int tcp_keepidle = 50; + int tcp_keepintvl = 50; +#ifdef WIN32 + DWORD outputBytes; +#endif + +#ifndef WIN32 + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive))) + return 1; +#ifdef __linux + if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &tcp_keepcnt, sizeof(tcp_keepcnt))) + return 1; + if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &tcp_keepidle, sizeof(tcp_keepidle))) + return 1; + if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &tcp_keepintvl, sizeof(tcp_keepintvl))) + return 1; +#endif /* __linux */ +#ifdef __APPLE_CC__ + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &tcp_keepintvl, sizeof(tcp_keepintvl))) + return 1; +#endif /* __APPLE_CC__ */ +#else /* WIN32 */ + struct tcp_keepalive vals; + vals.onoff = 1; + vals.keepalivetime = tcp_keepidle * 1000; + vals.keepaliveinterval = tcp_keepintvl * 1000; + if (unlikely(WSAIoctl(fd, SIO_KEEPALIVE_VALS, &vals, sizeof(vals), + NULL, 0, &outputBytes, NULL, NULL))) + return 1; +#endif /* WIN32 */ + + return 0; +} +#endif + + +static json_value *curl_json_rpc(YAAMP_RPC *rpc, const char *url, const char *rpc_req, int *curl_err) +{ + char len_hdr[64], auth_hdr[64]; + char curl_err_str[CURL_ERROR_SIZE] = { 0 }; + struct data_buffer all_data = { 0 }; + struct upload_buffer upload_data; + struct curl_slist *headers = NULL; + struct header_info hi = { 0 }; + char *httpdata; + json_value *val, *err_val, *res_val; + int rc; + + long timeout = opt_timeout; + bool keepalive = false; + + /* it is assumed that 'curl' is freshly [re]initialized at this pt */ + + if (opt_protocol) + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + curl_easy_setopt(curl, CURLOPT_URL, url); + + if (rpc->ssl) { + curl_easy_setopt(curl, CURLOPT_SSLVERSION, 1); // TLSv1 + if (strlen(rpc->cert)) + curl_easy_setopt(curl, CURLOPT_CAINFO, rpc->cert); + } + + curl_easy_setopt(curl, CURLOPT_ENCODING, ""); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 0); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, all_data_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &all_data); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, upload_data_cb); + curl_easy_setopt(curl, CURLOPT_READDATA, &upload_data); +#if LIBCURL_VERSION_NUM >= 0x071200 + curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, &seek_data_cb); + curl_easy_setopt(curl, CURLOPT_SEEKDATA, &upload_data); +#endif + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_err_str); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, resp_hdr_cb); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &hi); + if (opt_proxy) { + curl_easy_setopt(curl, CURLOPT_PROXY, opt_proxy); + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, opt_proxy_type); + } + +#if 0 + curl_easy_setopt(curl, CURLOPT_USERPWD, rpc->credential); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); +#else + sprintf(auth_hdr, "Authorization: Basic %s", rpc->credential); +#endif + +#if LIBCURL_VERSION_NUM >= 0x070f06 + if (keepalive) + curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_keepalive_cb); +#endif + curl_easy_setopt(curl, CURLOPT_POST, 1); + + if (opt_protocol) + debuglog("JSON protocol request:\n%s", rpc_req); + + upload_data.buf = rpc_req; + upload_data.len = strlen(rpc_req); + upload_data.pos = 0; + sprintf(len_hdr, "Content-Length: %lu", (unsigned long) upload_data.len); + + headers = curl_slist_append(headers, "Content-Type: application/json"); + headers = curl_slist_append(headers, len_hdr); + headers = curl_slist_append(headers, auth_hdr); + headers = curl_slist_append(headers, "User-Agent: " USER_AGENT); + headers = curl_slist_append(headers, "Accept:"); /* disable Accept hdr*/ + headers = curl_slist_append(headers, "Expect:"); /* disable Expect hdr*/ + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + rc = curl_easy_perform(curl); + if (curl_err != NULL) + *curl_err = rc; + if (rc) { + if (rc != CURLE_OPERATION_TIMEDOUT) { + debuglog("ERR: HTTP request failed: %s", curl_err_str); + goto err_out; + } + } + + if (!all_data.buf || !all_data.len) { + debuglog("ERR: Empty data received in json_rpc_call."); + goto err_out; + } + + httpdata = (char*) all_data.buf; + + if (*httpdata != '{' && *httpdata != '[') { + long errcode = 0; + CURLcode c = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &errcode); + if (c == CURLE_OK && errcode == 401) { + debuglog("ERR: You are not authorized, check your login and password."); + goto err_out; + } + } + + val = json_parse(httpdata, strlen(httpdata)); + if (!val) { + debuglog("ERR: JSON decode failed!"); + if (opt_protocol) + debuglog("%s", httpdata); + goto err_out; + } + + if (opt_protocol) { + //char *s = json_dumps(val, JSON_INDENT(3)); + debuglog("JSON protocol response:\n%s\n", httpdata); + //free(s); + } + + /* JSON-RPC valid response returns a non-null 'result', + * and a null 'error'. */ + res_val = json_object_get(val, "result"); + err_val = json_object_get(val, "error"); + + if (!res_val || json_is_null(res_val) || + (err_val && !json_is_null(err_val))) { + char *s = NULL; + + if (err_val) { + s = json_dumps(err_val, 0); + json_value *msg = json_object_get(err_val, "message"); + json_value *err_code = json_object_get(err_val, "code"); + if (curl_err && json_integer_value(err_code)) + *curl_err = (int) json_integer_value(err_code); + + if (json_is_string(msg)) { + free(s); + s = strdup(json_string_value(msg)); + } + //json_decref(err_val); + } + else + s = strdup("(unknown reason)"); + + if (!curl_err || opt_debug) + debuglog("ERR: JSON-RPC call failed: %s", s); + + free(s); + + goto err_out; + } + +// if (hi.reason) +// json_object_set_new(val, "reject-reason", json_string(hi.reason)); + + databuf_free(&all_data); + curl_slist_free_all(headers); + curl_easy_reset(curl); + return val; + +err_out: + free(hi.lp_path); + free(hi.reason); + free(hi.stratum_url); + databuf_free(&all_data); + curl_slist_free_all(headers); + curl_easy_reset(curl); + return NULL; +} + + +//------------------------------------------------------------------------------------------------- + +bool rpc_curl_connected(YAAMP_RPC *rpc) +{ + return (curl != NULL); +} + +void rpc_curl_close(YAAMP_RPC *rpc) +{ + if(!rpc_curl_connected(rpc)) return; + + //made by rpc_close + //pthread_mutex_destroy(&rpc->mutex); + + curl_easy_cleanup(curl); + curl = NULL; + //rpc->sock = 0; +} + +bool rpc_curl_connect(YAAMP_RPC *rpc) +{ + rpc_curl_close(rpc); + + //made by rpc_connect + //yaamp_create_mutex(&rpc->mutex); + + rpc->id = 0; + rpc->bufpos = 0; + curl = curl_easy_init(); + + return true; +} + +/////////////////////////////////////////////////////////////////// + +int rpc_curl_send(YAAMP_RPC *rpc, const char *format, ...) +{ + if(!rpc_curl_connected(rpc)) return -1; + + char buffer[YAAMP_SMALLBUFSIZE] = { 0 }; + va_list args; + + va_start(args, format); + vsprintf(buffer, format, args); + va_end(args); + + int bytes = strlen(buffer); + if(bytes + rpc->bufpos > YAAMP_SMALLBUFSIZE) + return -1; + + memcpy(rpc->buffer + rpc->bufpos, buffer, bytes); + rpc->bufpos += bytes; + + return bytes; +} + +///////////////////////////////////////////////////////////////////////////////// + +static json_value *rpc_curl_do_call(YAAMP_RPC *rpc, char const *data) +{ + CommonLock(&rpc->mutex); + + char url[1024]; + int curl_err = 0; + sprintf(url, "http%s://%s:%d", rpc->ssl?"s":"", rpc->host, rpc->port); + json_value *res = curl_json_rpc(rpc, url, data, &curl_err); + + CommonUnlock(&rpc->mutex); + + return res; +} + +json_value *rpc_curl_call(YAAMP_RPC *rpc, char const *method, char const *params) +{ +// debuglog("%s: %s:%d %s\n", __func__, rpc->host, rpc->port, method); + + int s1 = current_timestamp(); + if (!curl) { + rpc_curl_connect(rpc); + } + + if(!rpc_curl_connected(rpc)) return NULL; + + int paramlen = params? strlen(params): 0; + + char *message = (char *)malloc(paramlen+1024); + if(!message) return NULL; + + if(params) + sprintf(message, "{\"method\":\"%s\",\"params\":%s,\"id\":\"%d\"}", method, params, ++rpc->id); + else + sprintf(message, "{\"method\":\"%s\",\"id\":\"%d\"}", method, ++rpc->id); + + json_value *json = rpc_curl_do_call(rpc, message); + + free(message); + if(!json) return NULL; + + int s2 = current_timestamp(); + if(s2-s1 > 2000) + debuglog("delay rpc_call %s:%d %s in %d ms\n", rpc->host, rpc->port, method, s2-s1); + + if(json->type != json_object) + { + json_value_free(json); + return NULL; + } + + rpc_curl_close(rpc); + + return json; +} diff --git a/stratum/sha3/makefile b/stratum/sha3/makefile index 9904691..ca7c7ed 100644 --- a/stratum/sha3/makefile +++ b/stratum/sha3/makefile @@ -1,10 +1,7 @@ CC=gcc -#CFLAGS=-c -g -I /usr/include/mysql -#LDFLAGS=-g - -CFLAGS=-c -O3 -I /usr/include/mysql -march=native +CFLAGS= -O3 -march=native LDFLAGS=-O2 SOURCES=sph_jh.c sph_blake.c sph_bmw.c sph_groestl.c sph_skein.c sph_keccak.c sph_luffa.c sph_cubehash.c sph_shavite.c \ @@ -20,10 +17,10 @@ $(OUTPUT): $(OBJECTS) ar rc $@ $(OBJECTS) .cpp.o: - $(CC) $(CFLAGS) $< + $(CC) $(CFLAGS) -c $< .c.o: - $(CC) $(CFLAGS) $< + $(CC) $(CFLAGS) -c $< clean: rm *.o diff --git a/stratum/socket.cpp b/stratum/socket.cpp index 575ea59..d885da7 100644 --- a/stratum/socket.cpp +++ b/stratum/socket.cpp @@ -63,14 +63,14 @@ json_value *socket_nextjson(YAAMP_SOCKET *s, YAAMP_CLIENT *client) s->buflen += len; s->buffer[s->buflen] = 0; -// if(client && client->logtraffic) -// stratumlog("recv: %s\n", s->buffer); + if(client && client->logtraffic) + stratumlog("recv: %d\n", s->buflen); // pthread_mutex_lock(&s->mutex); } - char *p = strchr(s->buffer, '}'); - if(!p) + char *b = strchr(s->buffer, '{'); + if(!b) { if(client) clientlog(client, "bad json"); @@ -79,6 +79,16 @@ json_value *socket_nextjson(YAAMP_SOCKET *s, YAAMP_CLIENT *client) return NULL; } + char *p = strchr(b, '}'); + if(!p) + { + if(client) + clientlog(client, "bad json end"); + + debuglog("%s\n", b); + return NULL; + } + p++; char saved = *p; @@ -87,15 +97,15 @@ json_value *socket_nextjson(YAAMP_SOCKET *s, YAAMP_CLIENT *client) if(client && client->logtraffic) stratumlog("%s, %s, %s, %s, recv: %s\n", client->sock->ip, client->username, client->password, g_current_algo->name, s->buffer); - int bytes = strlen(s->buffer); + int bytes = strlen(b); - json_value *json = json_parse(s->buffer, bytes); + json_value *json = json_parse(b, bytes); if(!json) { if(client) clientlog(client, "bad json parse"); - debuglog("%s\n", s->buffer); + debuglog("%s\n", b); return NULL; } diff --git a/stratum/stratum.h b/stratum/stratum.h index 6e8b64f..ee70be1 100644 --- a/stratum/stratum.h +++ b/stratum/stratum.h @@ -30,11 +30,9 @@ using namespace std; #include "json.h" #include "util.h" -#define json_typeof(json) ((json)->type) -#define json_is_array(json) (json && json_typeof(json) == json_array) - #define YAAMP_RESTARTDELAY (24*60*60) #define YAAMP_MAXJOBDELAY (2*60) +#define CURL_RPC_TIMEOUT (30) #define YAAMP_MS 1000 #define YAAMP_SEC 1000000 diff --git a/stratum/util.h b/stratum/util.h index d6b53a6..7ae91b4 100644 --- a/stratum/util.h +++ b/stratum/util.h @@ -123,7 +123,7 @@ static inline uint16_t le16dec(const void *pp) } #endif - - - - +static inline uint32_t bswap32(uint32_t x) { + __asm__ __volatile__ ("bswapl %0" : "=r" (x) : "0" (x)); + return x; +}