Move stratum sockets to curl infrastructure with locking around send+recv to begin support for proxies and ssl.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
diff --git a/cgminer.c b/cgminer.c
index c2d19b2..aa71c46 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 f9fc107..55dc2c7 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 42c1935..0140db2 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 f1d4def..7d7ca43 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 fb520ca..aec827d 100644
--- a/util.h
+++ b/util.h
@@ -7,7 +7,7 @@
#include <netinet/in.h>
#include <arpa/inet.h>
- #define SOCKETTYPE int
+ #define SOCKETTYPE long
#define SOCKETFAIL(a) ((a) < 0)
#define INVSOCK -1
#define INVINETADDR -1