Commit a8d693029a0721fc74cf80231b748810e54b30e7

Con Kolivas 2012-10-05T12:37:29

Move stratum sockets to curl infrastructure with locking around send+recv to begin support for proxies and ssl.

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