@ -26,20 +26,17 @@
@@ -26,20 +26,17 @@
# include <sys / socket.h>
# include <netinet / in.h>
# include <netinet / tcp.h>
# include <netdb.h>
# else
# include <winsock2.h>
# include <mstcpip.h>
# include <ws2tcpip.h>
# endif
# include "miner.h"
# include "elist.h"
# include "compat.h"
# if JANSSON_MAJOR_VERSION >= 2
# define JSON_LOADS(str, err_ptr) json_loads((str), 0, (err_ptr))
# else
# define JSON_LOADS(str, err_ptr) json_loads((str), (err_ptr))
# endif
# include "util.h"
bool successful_connect = false ;
struct timeval nettime ;
@ -58,6 +55,7 @@ struct header_info {
@@ -58,6 +55,7 @@ struct header_info {
char * lp_path ;
int rolltime ;
char * reason ;
char * stratum_url ;
bool hadrolltime ;
bool canroll ;
bool hadexpire ;
@ -187,13 +185,17 @@ static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data)
@@ -187,13 +185,17 @@ static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data)
val = NULL ;
}
if ( ! strcasecmp ( " X-Stratum " , key ) ) {
hi - > stratum_url = val ;
val = NULL ;
}
out :
free ( key ) ;
free ( val ) ;
return ptrlen ;
}
# ifdef CURL_HAS_SOCKOPT
int json_rpc_call_sockopt_cb ( void __maybe_unused * userdata , curl_socket_t fd ,
curlsocktype __maybe_unused purpose )
{
@ -241,7 +243,6 @@ int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
@@ -241,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 )
{
@ -265,7 +266,7 @@ json_t *json_rpc_call(CURL *curl, const char *url,
@@ -265,7 +266,7 @@ json_t *json_rpc_call(CURL *curl, const char *url,
{
long timeout = longpoll ? ( 60 * 60 ) : 60 ;
struct data_buffer all_data = { NULL , 0 } ;
struct header_info hi = { NULL , 0 , NULL , false , false , false } ;
struct header_info hi = { NULL , 0 , NULL , NULL , false , false , false } ;
char len_hdr [ 64 ] , user_agent_hdr [ 128 ] ;
char curl_err_str [ CURL_ERROR_SIZE ] ;
struct curl_slist * headers = NULL ;
@ -316,10 +317,8 @@ json_t *json_rpc_call(CURL *curl, const char *url,
@@ -316,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 )
@ -392,10 +391,20 @@ json_t *json_rpc_call(CURL *curl, const char *url,
@@ -392,10 +391,20 @@ json_t *json_rpc_call(CURL *curl, const char *url,
pool - > hdr_path = hi . lp_path ;
} else
pool - > hdr_path = NULL ;
} else if ( hi . lp_path ) {
if ( hi . stratum_url ) {
pool - > stratum_url = hi . stratum_url ;
hi . stratum_url = NULL ;
}
} else {
if ( hi . lp_path ) {
free ( hi . lp_path ) ;
hi . lp_path = NULL ;
}
if ( hi . stratum_url ) {
free ( hi . stratum_url ) ;
hi . stratum_url = NULL ;
}
}
* rolltime = hi . rolltime ;
pool - > cgminer_pool_stats . rolltime = hi . rolltime ;
@ -794,3 +803,578 @@ double tdiff(struct timeval *end, struct timeval *start)
@@ -794,3 +803,578 @@ double tdiff(struct timeval *end, struct timeval *start)
{
return end - > tv_sec - start - > tv_sec + ( end - > tv_usec - start - > tv_usec ) / 1000000.0 ;
}
bool extract_sockaddr ( struct pool * pool , char * url )
{
char * url_begin , * url_end , * port_start = NULL ;
char url_address [ 256 ] , port [ 6 ] ;
struct addrinfo hints , * res ;
int url_len , port_len = 0 ;
url_begin = strstr ( url , " // " ) ;
if ( ! url_begin )
url_begin = url ;
else
url_begin + = 2 ;
url_end = strstr ( url_begin , " : " ) ;
if ( url_end ) {
url_len = url_end - url_begin ;
port_len = strlen ( url_begin ) - url_len - 1 ;
if ( port_len < 1 )
return false ;
port_start = url_end + 1 ;
} else
url_len = strlen ( url_begin ) ;
if ( url_len < 1 )
return false ;
sprintf ( url_address , " %.*s " , url_len , url_begin ) ;
if ( port_len )
snprintf ( port , 6 , " %.*s " , port_len , port_start ) ;
else
strcpy ( port , " 80 " ) ;
pool - > stratum_port = strdup ( port ) ;
memset ( & hints , 0 , sizeof ( struct addrinfo ) ) ;
hints . ai_family = AF_UNSPEC ;
hints . ai_socktype = SOCK_STREAM ;
hints . ai_protocol = IPPROTO_TCP ;
if ( getaddrinfo ( url_address , port , & hints , & res ) ) {
applog ( LOG_DEBUG , " Failed to extract sock addr " ) ;
return false ;
}
pool - > server = ( struct sockaddr_in * ) res - > ai_addr ;
pool - > sockaddr_url = strdup ( url_address ) ;
return true ;
}
/* Send a single command across a socket, appending \n to it */
bool stratum_send ( struct pool * pool , char * s , ssize_t len )
{
ssize_t ssent = 0 ;
bool ret = false ;
if ( opt_protocol )
applog ( LOG_DEBUG , " SEND: %s " , s ) ;
strcat ( s , " \n " ) ;
len + + ;
mutex_lock ( & pool - > stratum_lock ) ;
while ( len > 0 ) {
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 ;
}
ssent + = sent ;
len - = ssent ;
}
ret = true ;
out_unlock :
mutex_unlock ( & pool - > stratum_lock ) ;
return ret ; ;
}
# define RECVSIZE 8191
# define RBUFSIZE (RECVSIZE + 1)
static void clear_sock ( struct pool * pool )
{
SOCKETTYPE sock = pool - > sock ;
recv ( sock , pool - > sockbuf , RECVSIZE , MSG_DONTWAIT ) ;
strcpy ( pool - > sockbuf , " " ) ;
}
/* Check to see if Santa's been good to you */
static bool sock_full ( struct pool * pool , bool wait )
{
SOCKETTYPE sock = pool - > sock ;
struct timeval timeout ;
fd_set rd ;
if ( strlen ( pool - > sockbuf ) )
return true ;
FD_ZERO ( & rd ) ;
FD_SET ( sock , & rd ) ;
timeout . tv_usec = 0 ;
if ( wait )
timeout . tv_sec = 60 ;
else
timeout . tv_sec = 0 ;
if ( select ( sock + 1 , & rd , NULL , NULL , & timeout ) > 0 )
return true ;
return false ;
}
/* Peeks at a socket to find the first end of line and then reads just that
* from the socket and returns that as a malloced char */
char * recv_line ( struct pool * pool )
{
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 ) ;
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 ;
}
strcat ( pool - > sockbuf , s ) ;
}
buflen = strlen ( pool - > sockbuf ) ;
tok = strtok ( pool - > sockbuf , " \n " ) ;
if ( ! tok ) {
applog ( LOG_DEBUG , " Failed to parse a \\ n terminated string in recv_line " ) ;
goto out ;
}
sret = strdup ( tok ) ;
len = strlen ( sret ) ;
/* Copy what's left in the buffer after the \n, including the
* terminating \ 0 */
if ( buflen > len + 1 )
memmove ( pool - > sockbuf , pool - > sockbuf + len + 1 , buflen - len + 1 ) ;
else
strcpy ( pool - > sockbuf , " " ) ;
out :
if ( ! sret )
clear_sock ( pool ) ;
else if ( opt_protocol )
applog ( LOG_DEBUG , " RECVD: %s " , sret ) ;
return sret ;
}
/* Extracts a string value from a json array with error checking. To be used
* when the value of the string returned is only examined and not to be stored .
* See json_array_string below */
static char * __json_array_string ( json_t * val , unsigned int entry )
{
json_t * arr_entry ;
if ( json_is_null ( val ) )
return NULL ;
if ( ! json_is_array ( val ) )
return NULL ;
if ( entry > json_array_size ( val ) )
return NULL ;
arr_entry = json_array_get ( val , entry ) ;
if ( ! json_is_string ( arr_entry ) )
return NULL ;
return ( char * ) json_string_value ( arr_entry ) ;
}
/* Creates a freshly malloced dup of __json_array_string */
static char * json_array_string ( json_t * val , unsigned int entry )
{
char * buf = __json_array_string ( val , entry ) ;
if ( buf )
return strdup ( buf ) ;
return NULL ;
}
static bool parse_notify ( struct pool * pool , json_t * val )
{
char * job_id , * prev_hash , * coinbase1 , * coinbase2 , * bbversion , * nbit , * ntime ;
int merkles , i ;
json_t * arr ;
bool clean ;
arr = json_array_get ( val , 4 ) ;
if ( ! arr | | ! json_is_array ( arr ) )
return false ;
merkles = json_array_size ( arr ) ;
job_id = json_array_string ( val , 0 ) ;
prev_hash = json_array_string ( val , 1 ) ;
coinbase1 = json_array_string ( val , 2 ) ;
coinbase2 = json_array_string ( val , 3 ) ;
bbversion = json_array_string ( val , 5 ) ;
nbit = json_array_string ( val , 6 ) ;
ntime = json_array_string ( val , 7 ) ;
clean = json_is_true ( json_array_get ( val , 8 ) ) ;
if ( ! job_id | | ! prev_hash | | ! coinbase1 | | ! coinbase2 | | ! bbversion | | ! nbit | | ! ntime ) {
/* Annoying but we must not leak memory */
if ( job_id )
free ( job_id ) ;
if ( prev_hash )
free ( prev_hash ) ;
if ( coinbase1 )
free ( coinbase1 ) ;
if ( coinbase2 )
free ( coinbase2 ) ;
if ( bbversion )
free ( bbversion ) ;
if ( nbit )
free ( nbit ) ;
if ( ntime )
free ( ntime ) ;
return false ;
}
mutex_lock ( & pool - > pool_lock ) ;
pool - > swork . job_id = job_id ;
pool - > swork . prev_hash = prev_hash ;
pool - > swork . coinbase1 = coinbase1 ;
pool - > swork . coinbase2 = coinbase2 ;
pool - > swork . bbversion = bbversion ;
pool - > swork . nbit = nbit ;
pool - > swork . ntime = ntime ;
pool - > swork . clean = clean ;
for ( i = 0 ; i < pool - > swork . merkles ; i + + )
free ( pool - > swork . merkle [ i ] ) ;
if ( merkles ) {
pool - > swork . merkle = realloc ( pool - > swork . merkle , sizeof ( char * ) * merkles + 1 ) ;
for ( i = 0 ; i < merkles ; i + + )
pool - > swork . merkle [ i ] = json_array_string ( arr , i ) ;
}
pool - > swork . merkles = merkles ;
if ( clean )
pool - > nonce2 = 0 ;
mutex_unlock ( & pool - > pool_lock ) ;
if ( opt_protocol ) {
applog ( LOG_DEBUG , " job_id: %s " , job_id ) ;
applog ( LOG_DEBUG , " prev_hash: %s " , prev_hash ) ;
applog ( LOG_DEBUG , " coinbase1: %s " , coinbase1 ) ;
applog ( LOG_DEBUG , " coinbase2: %s " , coinbase2 ) ;
for ( i = 0 ; i < merkles ; i + + )
applog ( LOG_DEBUG , " merkle%d: %s " , i , pool - > swork . merkle [ i ] ) ;
applog ( LOG_DEBUG , " bbversion: %s " , bbversion ) ;
applog ( LOG_DEBUG , " nbit: %s " , nbit ) ;
applog ( LOG_DEBUG , " ntime: %s " , ntime ) ;
applog ( LOG_DEBUG , " clean: %s " , clean ? " yes " : " no " ) ;
}
/* A notify message is the closest stratum gets to a getwork */
pool - > getwork_requested + + ;
total_getworks + + ;
return true ;
}
static bool parse_diff ( struct pool * pool , json_t * val )
{
int diff ;
diff = json_integer_value ( json_array_get ( val , 0 ) ) ;
if ( diff < 1 )
return false ;
mutex_lock ( & pool - > pool_lock ) ;
pool - > swork . diff = diff ;
mutex_unlock ( & pool - > pool_lock ) ;
applog ( LOG_DEBUG , " Pool %d difficulty set to %d " , pool - > pool_no , diff ) ;
return true ;
}
static bool parse_reconnect ( struct pool * pool , json_t * val )
{
char * url , * port , address [ 256 ] ;
memset ( address , 0 , 255 ) ;
url = ( char * ) json_string_value ( json_array_get ( val , 0 ) ) ;
if ( ! url )
url = pool - > sockaddr_url ;
port = ( char * ) json_string_value ( json_array_get ( val , 1 ) ) ;
if ( ! port )
port = pool - > stratum_port ;
sprintf ( address , " %s:%s " , url , port ) ;
if ( ! extract_sockaddr ( pool , address ) )
return false ;
pool - > stratum_url = pool - > sockaddr_url ;
applog ( LOG_NOTICE , " Reconnect requested from pool %d to %s " , pool - > pool_no , address ) ;
if ( ! initiate_stratum ( pool ) | | ! auth_stratum ( pool ) )
return false ;
return true ;
}
static bool send_version ( struct pool * pool , json_t * val )
{
char s [ RBUFSIZE ] ;
int id = json_integer_value ( json_object_get ( val , " id " ) ) ;
if ( ! id )
return false ;
sprintf ( s , " { \" id \" : %d, \" result \" : \" " PACKAGE " / " VERSION " \" , \" error \" : null} " , id ) ;
if ( ! stratum_send ( pool , s , strlen ( s ) ) )
return false ;
return true ;
}
bool parse_method ( struct pool * pool , char * s )
{
json_t * val = NULL , * method , * err_val , * params ;
json_error_t err ;
bool ret = false ;
char * buf ;
if ( ! s )
goto out ;
val = JSON_LOADS ( s , & err ) ;
if ( ! val ) {
applog ( LOG_INFO , " JSON decode failed(%d): %s " , err . line , err . text ) ;
goto out ;
}
method = json_object_get ( val , " method " ) ;
if ( ! method )
goto out ;
err_val = json_object_get ( val , " error " ) ;
params = json_object_get ( val , " params " ) ;
if ( err_val & & ! json_is_null ( err_val ) ) {
char * ss ;
if ( err_val )
ss = json_dumps ( err_val , JSON_INDENT ( 3 ) ) ;
else
ss = strdup ( " (unknown reason) " ) ;
applog ( LOG_INFO , " JSON-RPC method decode failed: %s " , ss ) ;
free ( ss ) ;
goto out ;
}
buf = ( char * ) json_string_value ( method ) ;
if ( ! buf )
goto out ;
if ( ! strncasecmp ( buf , " mining.notify " , 13 ) & & parse_notify ( pool , params ) ) {
ret = true ;
goto out ;
}
if ( ! strncasecmp ( buf , " mining.set_difficulty " , 21 ) & & parse_diff ( pool , params ) ) {
ret = true ;
goto out ;
}
if ( ! strncasecmp ( buf , " client.reconnect " , 16 ) & & parse_reconnect ( pool , params ) ) {
ret = true ;
goto out ;
}
if ( ! strncasecmp ( buf , " client.get_version " , 18 ) & & send_version ( pool , val ) ) {
ret = true ;
goto out ;
}
out :
if ( val )
json_decref ( val ) ;
return ret ;
}
bool auth_stratum ( struct pool * pool )
{
json_t * val = NULL , * res_val , * err_val ;
char s [ RBUFSIZE ] , * sret = NULL ;
json_error_t err ;
bool ret = false ;
sprintf ( s , " { \" id \" : %d, \" method \" : \" mining.authorize \" , \" params \" : [ \" %s \" , \" %s \" ]} " ,
swork_id + + , pool - > rpc_user , pool - > rpc_pass ) ;
/* Parse all data prior sending auth request */
while ( sock_full ( pool , false ) ) {
sret = recv_line ( pool ) ;
if ( ! parse_method ( pool , sret ) ) {
clear_sock ( pool ) ;
applog ( LOG_INFO , " Failed to parse stratum buffer " ) ;
free ( sret ) ;
return ret ;
}
free ( sret ) ;
}
if ( ! stratum_send ( pool , s , strlen ( s ) ) )
goto out ;
sret = recv_line ( pool ) ;
if ( ! sret )
goto out ;
val = JSON_LOADS ( sret , & err ) ;
free ( sret ) ;
res_val = json_object_get ( val , " result " ) ;
err_val = json_object_get ( val , " error " ) ;
if ( ! res_val | | json_is_false ( res_val ) | | ( err_val & & ! json_is_null ( err_val ) ) ) {
char * ss ;
if ( err_val )
ss = json_dumps ( err_val , JSON_INDENT ( 3 ) ) ;
else
ss = strdup ( " (unknown reason) " ) ;
applog ( LOG_WARNING , " JSON stratum auth failed: %s " , ss ) ;
free ( ss ) ;
goto out ;
}
ret = true ;
applog ( LOG_INFO , " Stratum authorisation success for pool %d " , pool - > pool_no ) ;
out :
if ( val )
json_decref ( val ) ;
return ret ;
}
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_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 ;
/* 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 ) ;
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_INFO , " Stratum connect failed to pool %d: %s " , pool - > pool_no , 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 " ) ;
goto out ;
}
if ( ! sock_full ( pool , true ) ) {
applog ( LOG_DEBUG , " Timed out waiting for response in initiate_stratum " ) ;
goto out ;
}
sret = recv_line ( pool ) ;
if ( ! sret )
goto out ;
val = JSON_LOADS ( sret , & err ) ;
free ( sret ) ;
if ( ! val ) {
applog ( LOG_INFO , " JSON decode failed(%d): %s " , err . line , err . text ) ;
goto out ;
}
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 * ss ;
if ( err_val )
ss = json_dumps ( err_val , JSON_INDENT ( 3 ) ) ;
else
ss = strdup ( " (unknown reason) " ) ;
applog ( LOG_INFO , " JSON-RPC decode failed: %s " , ss ) ;
free ( ss ) ;
goto out ;
}
pool - > nonce1 = json_array_string ( res_val , 1 ) ;
if ( ! pool - > nonce1 ) {
applog ( LOG_INFO , " Failed to get nonce1 in initiate_stratum " ) ;
goto out ;
}
pool - > n2size = json_integer_value ( json_array_get ( res_val , 2 ) ) ;
if ( ! pool - > n2size ) {
applog ( LOG_INFO , " Failed to get n2size in initiate_stratum " ) ;
goto out ;
}
ret = true ;
out :
if ( val )
json_decref ( val ) ;
if ( ret ) {
if ( ! pool - > stratum_url )
pool - > stratum_url = pool - > sockaddr_url ;
pool - > stratum_active = true ;
pool - > swork . diff = 1 ;
if ( opt_protocol ) {
applog ( LOG_DEBUG , " Pool %d confirmed mining.subscribe with extranonce1 %s extran2size %d " ,
pool - > pool_no , pool - > nonce1 , pool - > n2size ) ;
}
} else {
pool - > stratum_active = false ;
if ( curl ) {
curl_easy_cleanup ( curl ) ;
pool - > stratum_curl = NULL ;
}
}
return ret ;
}