diff --git a/cgminer.c b/cgminer.c index c2d19b27..aa71c466 100644 --- a/cgminer.c +++ b/cgminer.c @@ -432,6 +432,8 @@ struct pool *add_pool(void) quit(1, "Failed to pthread_mutex_init in add_pool"); if (unlikely(pthread_cond_init(&pool->cr_cond, NULL))) quit(1, "Failed to pthread_cond_init in add_pool"); + if (unlikely(pthread_mutex_init(&pool->stratum_lock, NULL))) + quit(1, "Failed to pthread_mutex_init in add_pool"); INIT_LIST_HEAD(&pool->curlring); /* Make sure the pool doesn't think we've been idle since time 0 */ @@ -562,12 +564,6 @@ static char *set_rr(enum pool_strategy *strategy) * stratum+tcp or by detecting a stratum server response */ bool detect_stratum(struct pool *pool, char *url) { - if (pool->rpc_proxy) { - if (!strncasecmp(url, "stratum+tcp://", 14)) - applog(LOG_WARNING, "Cannot use a stratum server with a proxy"); - return false; - } - if (!extract_sockaddr(pool, url)) return false; @@ -4249,7 +4245,7 @@ retry_stratum: /* Detect if a http getwork pool has an X-Stratum header at startup, * and if so, switch to that in preference to getwork. Currently no * proxy support so don't try to switch if a proxy is in use. */ - if (unlikely(pool->stratum_url && !pool->rpc_proxy)) { + if (unlikely(pool->stratum_url)) { applog(LOG_NOTICE, "Switching pool %d %s to %s", pool->pool_no, pool->rpc_url, pool->stratum_url); pool->has_stratum = true; pool->rpc_url = pool->stratum_url; diff --git a/configure.ac b/configure.ac index f9fc107a..55dc2c7e 100644 --- a/configure.ac +++ b/configure.ac @@ -352,8 +352,7 @@ fi PKG_PROG_PKG_CONFIG() -PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.15.6], [AC_DEFINE([CURL_HAS_SOCKOPT], [1], [Defined if version of curl supports sockopts.])], -[PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.10.1], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.10.1])])]) +PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.18.2], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.18.2])]) AC_SUBST(LIBCURL_LIBS) dnl CCAN wants to know a lot of vars. diff --git a/miner.h b/miner.h index 42c19351..0140db20 100644 --- a/miner.h +++ b/miner.h @@ -863,6 +863,8 @@ struct pool { /* Stratum variables */ char *stratum_url; + char *stratum_port; + CURL *stratum_curl; SOCKETTYPE sock; char sockbuf[RBUFSIZE]; struct sockaddr_in *server, client; @@ -875,6 +877,7 @@ struct pool { bool stratum_auth; struct stratum_work swork; pthread_t stratum_thread; + pthread_mutex_t stratum_lock; }; #define GETWORK_MODE_TESTPOOL 'T' diff --git a/util.c b/util.c index f1d4defd..7d7ca430 100644 --- a/util.c +++ b/util.c @@ -196,7 +196,6 @@ out: return ptrlen; } -#ifdef CURL_HAS_SOCKOPT int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd, curlsocktype __maybe_unused purpose) { @@ -244,7 +243,6 @@ int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd, return 0; } -#endif static void last_nettime(struct timeval *last) { @@ -319,10 +317,8 @@ json_t *json_rpc_call(CURL *curl, const char *url, curl_easy_setopt(curl, CURLOPT_USERPWD, userpass); curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); } -#ifdef CURL_HAS_SOCKOPT if (longpoll) curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, json_rpc_call_sockopt_cb); -#endif curl_easy_setopt(curl, CURLOPT_POST, 1); if (opt_protocol) @@ -835,11 +831,12 @@ bool extract_sockaddr(struct pool *pool, char *url) sprintf(url_address, "%.*s", url_len, url_begin); - if (port_len) { + if (port_len) sprintf(port, "%.*s", port_len, port_start); - } else { + else strcpy(port, "80"); - } + + pool->stratum_port = strdup(port); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; @@ -859,8 +856,7 @@ bool extract_sockaddr(struct pool *pool, char *url) /* Send a single command across a socket, appending \n to it */ bool stratum_send(struct pool *pool, char *s, ssize_t len) { - SOCKETTYPE sock = pool->sock; - ssize_t sent = 0; + ssize_t ssent = 0; bool ret = false; if (opt_protocol) @@ -869,19 +865,21 @@ bool stratum_send(struct pool *pool, char *s, ssize_t len) strcat(s, "\n"); len++; - mutex_lock(&pool->pool_lock); + mutex_lock(&pool->stratum_lock); while (len > 0 ) { - sent = send(sock, s + sent, len, 0); - if (SOCKETFAIL(sent)) { + size_t sent = 0; + + if (curl_easy_send(pool->stratum_curl, s + ssent, len, &sent) != CURLE_OK) { + applog(LOG_DEBUG, "Failed to curl_easy_send in stratum_send"); ret = false; goto out_unlock; } - len -= sent; + ssent += sent; + len -= ssent; } ret = true; - fsync(sock); out_unlock: - mutex_unlock(&pool->pool_lock); + mutex_unlock(&pool->stratum_lock); return ret;; } @@ -922,15 +920,25 @@ static bool sock_full(struct pool *pool, bool wait) * from the socket and returns that as a malloced char */ char *recv_line(struct pool *pool) { - SOCKETTYPE sock = pool->sock; ssize_t len, buflen; char *tok, *sret = NULL; + size_t n; if (!strstr(pool->sockbuf, "\n")) { char s[RBUFSIZE]; + CURLcode rc; + if (!sock_full(pool, true)) { + applog(LOG_DEBUG, "Timed out waiting for data on sock_full"); + goto out; + } memset(s, 0, RBUFSIZE); - if (SOCKETFAIL(recv(sock, s, RECVSIZE, 0))) { + + mutex_lock(&pool->stratum_lock); + rc = curl_easy_recv(pool->stratum_curl, s, RECVSIZE, &n); + mutex_unlock(&pool->stratum_lock); + + if (rc != CURLE_OK) { applog(LOG_DEBUG, "Failed to recv sock in recv_line"); goto out; } @@ -1202,22 +1210,48 @@ out: bool initiate_stratum(struct pool *pool) { json_t *val = NULL, *res_val, *err_val; + char curl_err_str[CURL_ERROR_SIZE]; char s[RBUFSIZE], *sret = NULL; + CURL *curl = NULL; json_error_t err; bool ret = false; if (pool->stratum_active) return true; - sprintf(s, "{\"id\": %d, \"method\": \"mining.subscribe\", \"params\": []}", swork_id++); + if (!pool->stratum_curl) { + pool->stratum_curl = curl_easy_init(); + if (unlikely(!pool->stratum_curl)) + quit(1, "Failed to curl_easy_init in initiate_stratum"); + } + curl = pool->stratum_curl; - pool->sock = socket(AF_INET, SOCK_STREAM, 0); - if (pool->sock == INVSOCK) - quit(1, "Failed to create pool socket in initiate_stratum"); - if (SOCKETFAIL(connect(pool->sock, (struct sockaddr *)pool->server, sizeof(struct sockaddr)))) { - applog(LOG_DEBUG, "Failed to connect socket to pool"); + /* Create a http url for use with curl */ + memset(s, 0, RBUFSIZE); + sprintf(s, "http://%s:%s", pool->sockaddr_url, pool->stratum_port); + + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 60); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_err_str); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_URL, s); + curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1); + curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); + curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1); + if (pool->rpc_proxy) { + curl_easy_setopt(curl, CURLOPT_PROXY, pool->rpc_proxy); + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, pool->rpc_proxytype); + } else if (opt_socks_proxy) { + curl_easy_setopt(curl, CURLOPT_PROXY, opt_socks_proxy); + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); + } + curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1); + if (curl_easy_perform(curl)) { + applog(LOG_ERR, "Stratum connect failed: %s", curl_err_str); goto out; } + curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, (long *)&pool->sock); + + sprintf(s, "{\"id\": %d, \"method\": \"mining.subscribe\", \"params\": []}", swork_id++); if (!stratum_send(pool, s, strlen(s))) { applog(LOG_DEBUG, "Failed to send s in initiate_stratum"); @@ -1283,8 +1317,12 @@ out: applog(LOG_DEBUG, "Pool %d confirmed mining.subscribe with extranonce1 %s extran2size %d", pool->pool_no, pool->nonce1, pool->n2size); } - } else - CLOSESOCKET(pool->sock); + } else { + if (curl) { + curl_easy_cleanup(curl); + pool->stratum_curl = NULL; + } + } return ret; } diff --git a/util.h b/util.h index fb520ca7..aec827d4 100644 --- a/util.h +++ b/util.h @@ -7,7 +7,7 @@ #include #include - #define SOCKETTYPE int + #define SOCKETTYPE long #define SOCKETFAIL(a) ((a) < 0) #define INVSOCK -1 #define INVINETADDR -1