Browse Source

Merge branch 'master' of bitbucket.org:ckolivas/ckpool

master
Con Kolivas 9 years ago
parent
commit
d23dc06629
  1. 13
      configure.ac
  2. 12
      pool/base.php
  3. 51
      pool/db.php
  4. 2
      pool/inc.php
  5. 60
      pool/page.php
  6. 121
      pool/page_2fa.php
  7. 22
      pool/page_addrmgt.php
  8. 7
      pool/page_allwork.php
  9. 4
      pool/page_blocks.php
  10. 68
      pool/page_payout.php
  11. 50
      pool/page_reg.php
  12. 7
      pool/page_reset.php
  13. 45
      pool/page_settings.php
  14. 65
      pool/page_workers.php
  15. 5
      pool/prime.php
  16. 5
      sql/ckdb.sql
  17. 38
      sql/v1.0.0-v1.0.1.sql
  18. 4
      src/Makefile.am
  19. 2
      src/ckdb.c
  20. 131
      src/ckdb.h
  21. 330
      src/ckdb_cmd.c
  22. 239
      src/ckdb_crypt.c
  23. 331
      src/ckdb_data.c
  24. 189
      src/ckdb_dbio.c
  25. 55
      src/ckpool.c
  26. 22
      src/libckpool.c

13
configure.ac

@ -39,6 +39,7 @@ AC_CHECK_HEADERS(sys/types.h sys/socket.h sys/stat.h linux/un.h netdb.h)
AC_CHECK_HEADERS(stdint.h netinet/in.h netinet/tcp.h sys/ioctl.h getopt.h) AC_CHECK_HEADERS(stdint.h netinet/in.h netinet/tcp.h sys/ioctl.h getopt.h)
AC_CHECK_HEADERS(sys/epoll.h libpq-fe.h postgresql/libpq-fe.h grp.h) AC_CHECK_HEADERS(sys/epoll.h libpq-fe.h postgresql/libpq-fe.h grp.h)
AC_CHECK_HEADERS(gsl/gsl_math.h gsl/gsl_cdf.h) AC_CHECK_HEADERS(gsl/gsl_math.h gsl/gsl_cdf.h)
AC_CHECK_HEADERS(openssl/x509.h openssl/hmac.h)
PTHREAD_LIBS="-lpthread" PTHREAD_LIBS="-lpthread"
MATH_LIBS="-lm" MATH_LIBS="-lm"
@ -64,13 +65,17 @@ if test "x$ckdb" != "xno"; then
not found. Install it or disable support with --without-ckdb" && exit 1) not found. Install it or disable support with --without-ckdb" && exit 1)
AC_CHECK_LIB([gslcblas], [main],[GSLCBLAS=-lgslcblas],echo "Error: Required library gslcblas AC_CHECK_LIB([gslcblas], [main],[GSLCBLAS=-lgslcblas],echo "Error: Required library gslcblas
not found. Install it or disable support with --without-ckdb" && exit 1) not found. Install it or disable support with --without-ckdb" && exit 1)
AC_CHECK_LIB([ssl], [main],[SSL=-lssl],echo "Error: Required library ssl
not found. Install it or disable support with --without-ckdb" && exit 1)
AC_CHECK_LIB([crypto], [main],[SSL=-lcrypto],echo "Error: Required library crypto
not found. Install it or disable support with --without-ckdb" && exit 1)
AC_DEFINE([USE_CKDB], [1], [Defined to 1 if ckdb support required]) AC_DEFINE([USE_CKDB], [1], [Defined to 1 if ckdb support required])
PQ_LIBS="-lpq -lgsl -lgslcblas" DB_LIBS="-lpq -lgsl -lgslcblas -lssl -lcrypto"
else else
PQ_LIBS="" DB_LIBS=""
fi fi
AM_CONDITIONAL([WANT_CKDB], [test "x$ckdb" != "xno"]) AM_CONDITIONAL([WANT_CKDB], [test "x$ckdb" != "xno"])
AC_SUBST(PQ_LIBS) AC_SUBST(DB_LIBS)
AC_OUTPUT([Makefile] [src/Makefile]) AC_OUTPUT([Makefile] [src/Makefile])
@ -80,7 +85,7 @@ echo " CPPFLAGS.............: $CPPFLAGS"
echo " CFLAGS...............: $CFLAGS" echo " CFLAGS...............: $CFLAGS"
echo " LDFLAGS..............: $LDFLAGS" echo " LDFLAGS..............: $LDFLAGS"
echo " LDADD................: $PTHREAD_LIBS $MATH_LIBS $RT_LIBS $JANSSON_LIBS" echo " LDADD................: $PTHREAD_LIBS $MATH_LIBS $RT_LIBS $JANSSON_LIBS"
echo " db LDADD.............: $PQ_LIBS" echo " db LDADD.............: $DB_LIBS"
echo echo
echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')" echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')"
echo " prefix...............: $prefix" echo " prefix...............: $prefix"

12
pool/base.php

@ -323,12 +323,12 @@ session_start();
# #
include_once('db.php'); include_once('db.php');
# #
function validUserPass($user, $pass) function validUserPass($user, $pass, $twofa)
{ {
$rep = checkPass($user, $pass); $rep = checkPass($user, $pass, $twofa);
if ($rep != null) if ($rep != null)
$ans = repDecode($rep); $ans = repDecode($rep);
usleep(100000); // Max 10x per second usleep(500000); // Max twice per second
if ($rep != null && $ans['STATUS'] == 'ok') if ($rep != null && $ans['STATUS'] == 'ok')
{ {
$key = 'ckp'.rand(1000000,9999999); $key = 'ckp'.rand(1000000,9999999);
@ -352,7 +352,7 @@ function logout()
} }
} }
# #
function requestRegister() function requestLoginRegReset()
{ {
$reg = getparam('Register', true); $reg = getparam('Register', true);
$reg2 = getparam('Reset', false); $reg2 = getparam('Reset', false);
@ -397,7 +397,9 @@ function tryLogInOut()
return; return;
} }
$valid = validUserPass($user, $pass); $twofa = getparam('2fa', false);
$valid = validUserPass($user, $pass, $twofa);
if (!$valid) if (!$valid)
$loginfailed = true; $loginfailed = true;
} }

51
pool/db.php

@ -166,23 +166,18 @@ function homeInfo($user)
if ($rep === false) if ($rep === false)
$ans = false; $ans = false;
else else
{
$ans = repDecode($rep); $ans = repDecode($rep);
// if ($ans['lastblock'] == '?')
// {
// $ans['lastblock'] = 1401237522;
// $ans['lastblock'] = 1403819191;
// $ans['lastblock'] = 1407113822;
// }
}
return $ans; return $ans;
} }
# #
function checkPass($user, $pass) function checkPass($user, $pass, $twofa)
{ {
$passhash = myhash($pass); $passhash = myhash($pass);
$flds = array('username' => $user, 'passwordhash' => $passhash); if (nuem($twofa))
$twofa = 0;
$flds = array('username' => $user, 'passwordhash' => $passhash,
'2fa' => $twofa);
$msg = msgEncode('chkpass', 'chkpass', $flds, $user); $msg = msgEncode('chkpass', 'chkpass', $flds, $user);
$rep = sendsockreply('checkPass', $msg); $rep = sendsockreply('checkPass', $msg);
if (!$rep) if (!$rep)
@ -190,11 +185,14 @@ function checkPass($user, $pass)
return $rep; return $rep;
} }
# #
function setPass($user, $oldpass, $newpass) function setPass($user, $oldpass, $newpass, $twofa)
{ {
$oldhash = myhash($oldpass); $oldhash = myhash($oldpass);
$newhash = myhash($newpass); $newhash = myhash($newpass);
$flds = array('username' => $user, 'oldhash' => $oldhash, 'newhash' => $newhash); if (nuem($twofa))
$twofa = 0;
$flds = array('username' => $user, 'oldhash' => $oldhash,
'newhash' => $newhash, '2fa' => $twofa);
$msg = msgEncode('newpass', 'newpass', $flds, $user); $msg = msgEncode('newpass', 'newpass', $flds, $user);
$rep = sendsockreply('setPass', $msg); $rep = sendsockreply('setPass', $msg);
if (!$rep) if (!$rep)
@ -202,10 +200,12 @@ function setPass($user, $oldpass, $newpass)
return repDecode($rep); return repDecode($rep);
} }
# #
function resetPass($user, $newpass) function resetPass($user, $newpass, $twofa)
{ {
$newhash = myhash($newpass); $newhash = myhash($newpass);
$flds = array('username' => $user, 'newhash' => $newhash); if (nuem($twofa))
$twofa = 0;
$flds = array('username' => $user, 'newhash' => $newhash, '2fa' => $twofa);
$msg = msgEncode('newpass', 'newpass', $flds, $user); $msg = msgEncode('newpass', 'newpass', $flds, $user);
$rep = sendsockreply('resetPass', $msg); $rep = sendsockreply('resetPass', $msg);
if (!$rep) if (!$rep)
@ -213,10 +213,24 @@ function resetPass($user, $newpass)
return repDecode($rep); return repDecode($rep);
} }
# #
function get2fa($user, $action, $entropy, $value)
{
if ($value === null)
$value = '';
$flds = array('username' => $user, 'action' => $action,
'entropy' => $entropy, 'value' => $value);
$msg = msgEncode('2fa', '2fa', $flds, $user);
$rep = sendsockreply('get2fa', $msg);
if (!$rep)
dbdown();
return repDecode($rep);
}
#
function userReg($user, $email, $pass) function userReg($user, $email, $pass)
{ {
$passhash = myhash($pass); $passhash = myhash($pass);
$flds = array('username' => $user, 'emailaddress' => $email, 'passwordhash' => $passhash); $flds = array('username' => $user, 'emailaddress' => $email,
'passwordhash' => $passhash);
$msg = msgEncode('adduser', 'reg', $flds, $user); $msg = msgEncode('adduser', 'reg', $flds, $user);
$rep = sendsockreply('userReg', $msg); $rep = sendsockreply('userReg', $msg);
if (!$rep) if (!$rep)
@ -224,7 +238,7 @@ function userReg($user, $email, $pass)
return repDecode($rep); return repDecode($rep);
} }
# #
function userSettings($user, $email = null, $addr = null, $pass = null) function userSettings($user, $email = null, $addr = null, $pass = null, $twofa = null)
{ {
$tmo = false; $tmo = false;
$flds = array('username' => $user); $flds = array('username' => $user);
@ -246,7 +260,12 @@ function userSettings($user, $email = null, $addr = null, $pass = null)
$tmo = 3; # 3x the timeout $tmo = 3; # 3x the timeout
} }
if ($pass != null) if ($pass != null)
{
$flds['passwordhash'] = myhash($pass); $flds['passwordhash'] = myhash($pass);
if (nuem($twofa))
$twofa = 0;
$flds['2fa'] = $twofa;
}
$msg = msgEncode('usersettings', 'userset', $flds, $user); $msg = msgEncode('usersettings', 'userset', $flds, $user);
$rep = sendsockreply('userSettings', $msg, $tmo); $rep = sendsockreply('userSettings', $msg, $tmo);
if (!$rep) if (!$rep)

2
pool/inc.php

@ -91,6 +91,8 @@ span.login {float:right;margin-left:8px;margin-right:24px;}
span.hil {color:blue;} span.hil {color:blue;}
span.user {color:green;} span.user {color:green;}
span.addr {color:brown;} span.addr {color:brown;}
span.hdr {font-weight:bold;text-decoration:underline;}
span.nn {font-weight:bold;color:red;}
span.warn {color:orange;font-weight:bold;} span.warn {color:orange;font-weight:bold;}
span.urg {color:red;font-weight:bold;} span.urg {color:red;font-weight:bold;}
span.err {color:red;font-weight:bold;font-size:120%;} span.err {color:red;font-weight:bold;font-size:120%;}

60
pool/page.php

@ -165,6 +165,7 @@ function pgtop($info, $dotop, $user, $douser)
$nlb = '?'; $nlb = '?';
$pac = '0'; $pac = '0';
$per = '0'; $per = '0';
$perset = false;
$uhr = '?GHs'; $uhr = '?GHs';
$u1hr = ''; $u1hr = '';
if ($info !== false) if ($info !== false)
@ -244,12 +245,46 @@ function pgtop($info, $dotop, $user, $douser)
} }
} }
if (isset($info['blockshareinv']))
{
$shinv = $info['blockshareinv'];
$per = siprefmt($shinv, 1);
$perset = true;
if (isset($info['blockshareacc']))
{
$shacc = $info['blockshareacc'];
if (($shacc+$shinv) > 0)
{
$amt = 100.0 * $shinv / ($shacc + $shinv);
if (round($amt, 2) > 9.99)
$per .= ' ('.number_format($amt, 1).'%)';
else
$per .= ' ('.number_format($amt, 2).'%)';
}
}
}
if (isset($info['blockerr'])) if (isset($info['blockerr']))
{ {
$rej = $info['blockerr']; if ($perset == false)
$per = number_format($info['blockerr'], 0); $per = '';
if (isset($info['blockacc']) && ($acc+$rej) > 0) else
$per .= ' ('.number_format(100.0*$rej/($acc+$rej), 3).'%)'; $per .= ' · ';
$inv = $info['blockerr'];
$per .= siprefmt($inv, 1);
if (isset($info['blockacc']))
{
$acc = $info['blockacc'];
if (($acc+$inv) > 0)
{
$amt = 100.0 * $inv / ($acc + $inv);
if (round($amt, 2) > 9.99)
$per .= ' ('.number_format($amt, 1).'%)';
else
$per .= ' ('.number_format($amt, 2).'%)';
}
}
} }
if (isset($info['u_hashrate5m'])) if (isset($info['u_hashrate5m']))
@ -350,7 +385,7 @@ function pgtop($info, $dotop, $user, $douser)
$top .= "<td class=topdat>&nbsp;$phr</td></tr>"; $top .= "<td class=topdat>&nbsp;$phr</td></tr>";
$top .= '<tr><td class=topdes>Shares:&nbsp;</td>'; $top .= '<tr><td class=topdes>Shares:&nbsp;</td>';
$top .= "<td class=topdat>&nbsp;$pac</td></tr>"; $top .= "<td class=topdat>&nbsp;$pac</td></tr>";
$top .= '<tr id=mini1><td class=topdes>Invalid:&nbsp;</td>'; $top .= '<tr id=mini1><td class=topdes>Invalids:&nbsp;</td>';
$top .= "<td class=topdat>&nbsp;$per</td></tr></table>"; $top .= "<td class=topdat>&nbsp;$per</td></tr></table>";
$top .= '</td><td>'; $top .= '</td><td>';
$top .= '<table cellpadding=1 cellspacing=0 border=0 width=100%>'; $top .= '<table cellpadding=1 cellspacing=0 border=0 width=100%>';
@ -374,17 +409,10 @@ function pgtop($info, $dotop, $user, $douser)
list($who, $whoid) = validate(); list($who, $whoid) = validate();
if ($who == false) if ($who == false)
{ {
$top .= makeForm('')." $top .= '<table cellpadding=0 cellspacing=0 border=0><tr><td>';
<table cellpadding=0 cellspacing=0 border=0><tr><td> $top .= '<a href=https://' . $_SERVER['SERVER_NAME'];
<table cellpadding=0 cellspacing=0 border=0><tr> $top .= '/index.php?Register=1>Login<br>Register</a>';
<td>User:</td><td><input type=text name=User size=10 value=''></td> $top .= '</td></tr></table>';
</tr><tr>
<td>Pass:</td><td><input type=password name=Pass size=10 value=''></td>
</tr></table></td><td>
<table cellpadding=0 cellspacing=0 border=0><tr>
<td>&nbsp;<input type=submit name=Login value=Login></td></tr><tr>
<td>&nbsp;&nbsp;<input type=submit name=Register value='Register/Reset'></td></tr></table>
</td></tr></table></form>";
} }
else else
{ {

121
pool/page_2fa.php

@ -0,0 +1,121 @@
<?php
#
function set_2fa($data, $user, $tfa, $ans, $err)
{
$pg = '<h1>Two Factor Authentication Settings</h1>';
if ($err !== null and $err != '')
$pg .= "<span class=err>$err<br><br></span>";
$pg .= '<table cellpadding=20 cellspacing=0 border=1>';
$pg .= '<tr class=dc><td><center>';
$pg .= makeForm('2fa');
$pg .= '<table cellpadding=5 cellspacing=0 border=0>';
$pg .= '<tr class=dc><td>';
switch ($tfa)
{
case '':
$pg .= '<tr class=dl><td>';
$pg .= "You don't have 2FA setup yet<br><br>";
$pg .= 'To use 2FA you need an App on your phone/tablet<br>';
$pg .= 'The free and recommended ones that have been tested here are:<br><br>';
$pg .= "Android: Google Play 'FreeOTP Authenticator' by Red Hat<br>";
$pg .= "Apple: App Store 'OTP Auth' by Roland Moers<br><br>";
$pg .= 'Click here to start setting up 2FA: ';
$pg .= '<input type=submit name=Setup value=Setup>';
$pg .= '</td></tr>';
break;
case 'test':
$pg .= '<tr class=dc><td>';
$pg .= '2FA is not yet enabled.<br>';
$pg .= 'Your 2FA key has been created but needs testing.<br><br>';
if (isset($ans['2fa_key']))
{
$key = $ans['2fa_key'];
$sfainfo = $ans['2fa_issuer'].': '.$ans['2fa_auth'].' '.
$ans['2fa_hash'].' '.$ans['2fa_time'].'s';
$who = substr($user, 0, 8);
$sfaurl = 'otpauth://'.$ans['2fa_auth'].'/'.$ans['2fa_issuer'].
':'.htmlspecialchars($who).'?secret='.$ans['2fa_key'].
'&algorithm='.$ans['2fa_hash'].'&issuer='.$ans['2fa_issuer'];
}
else
{
$key = 'unavailable';
$sfainfo = 'unavailable';
$sfaurl = 'unavailable';
}
$pg .= "Your 2FA Secret Key is: $key<br>";
$pg .= "2FA Settings are $sfainfo<br><br>";
$pg .= "2FA URL is <a href='$sfaurl'>Click</a><br><br>";
$pg .= '2FA Value: <input name=Value value="" size=10> ';
$pg .= '<input type=submit name=Test value=Test>';
$pg .= '</td></tr>';
break;
case 'ok':
$pg .= '<tr class=dc><td>';
$pg .= '2FA is enabled on your account.<br><br>';
$pg .= 'If you wish to replace your Secret Key with a new one:<br><br>';
$pg .= 'Current 2FA Value: <input name=Value value="" size=10> ';
$pg .= '<input type=submit name=New value=New><span class=st1>*</span><br><br>';
$pg .= '<span class=st1>*</span>WARNING: replacing the Secret Key will disable 2FA<br>';
$pg .= 'until you successfully test the new key.<br><br>';
$pg .= '</td></tr>';
break;
}
$pg .= '</table></form>';
$pg .= '</center></td></tr>';
$pg .= '</table>';
return $pg;
}
#
function do2fa($data, $user)
{
$err = '';
$setup = getparam('Setup', false);
if ($setup === 'Setup')
{
// rand() included as part of the entropy
$ans = get2fa($user, 'setup', rand(1073741824,2147483647), 0);
}
else
{
$value = getparam('Value', false);
$test = getparam('Test', false);
if ($test === 'Test' and $value !== null)
$ans = get2fa($user, 'test', 0, $value);
else
{
$nw = getparam('New', false);
if ($nw === 'New' and $value !== null)
$ans = get2fa($user, 'new', rand(1073741824,2147483647), $value);
else
$ans = get2fa($user, '', 0, 0);
}
}
if ($ans['STATUS'] != 'ok')
$err = 'DBERR';
else
{
if (isset($ans['2fa_error']))
$err = $ans['2fa_error'];
}
if (!isset($ans['2fa_status']))
$tfa = null;
else
$tfa = $ans['2fa_status'];
$pg = set_2fa($data, $user, $tfa, $ans, $err);
return $pg;
}
#
function show_2fa($info, $page, $menu, $name, $user)
{
gopage($info, NULL, 'do2fa', $page, $menu, $name, $user);
}
#
?>

22
pool/page_addrmgt.php

@ -72,13 +72,24 @@ function addrmgtuser($data, $user, $err)
else else
$row = 'odd'; $row = 'odd';
$pg .= "<tr class=$row>"; $pg .= "<tr class=$row>";
$pg .= '<td colspan=3 class=dc>'; $pg .= '<td class=dr>';
$pg .= 'Password: <input type=password name=pass size=20>'; $pg .= 'Password: <input type=password name=pass size=20>';
$pg .= '&nbsp;<input type=submit name=OK value=Save></td></tr>'; $pg .= '</td><td colspan=2>&nbsp;</td></tr>';
}
$pg .= '<tr><td colspan=3 class=dc><font size=-1><span class=st1>*</span>'; if ((($offset++) % 2) == 0)
$row = 'even';
else
$row = 'odd';
$pg .= "<tr class=$row>";
$pg .= '<td class=dr>';
$pg .= '<span class=st1>*</span>2nd Authentication: <input type=password name=2fa size=10>';
$pg .= '</td><td colspan=2 class=dl><input type=submit name=OK value=Save></td></tr>';
$pg .= "<tr><td colspan=3 class=dc><font size=-1>";
$pg .= "<span class=st1>*</span>Leave blank if you haven't enabled it<br>";
$pg .= 'You must enter your password to save changes<br>'; $pg .= 'You must enter your password to save changes<br>';
$pg .= 'A ratio of 0, will remove the address from the payouts</font></td></tr>'; $pg .= 'A ratio of 0, will remove the address from the payouts</font></td></tr>';
}
$pg .= "</table><input type=hidden name=rows value=$count id=rows></form>\n"; $pg .= "</table><input type=hidden name=rows value=$count id=rows></form>\n";
$pg .= "<script type='text/javascript'>\n"; $pg .= "<script type='text/javascript'>\n";
@ -107,6 +118,7 @@ function doaddrmgt($data, $user)
$OK = getparam('OK', false); $OK = getparam('OK', false);
$count = getparam('rows', false); $count = getparam('rows', false);
$pass = getparam('pass', false); $pass = getparam('pass', false);
$twofa = getparam('2fa', false);
if ($OK == 'Save' && !nuem($count) && !nuem($pass)) if ($OK == 'Save' && !nuem($count) && !nuem($pass))
{ {
if ($count > 0 && $count < 1000) if ($count > 0 && $count < 1000)
@ -119,7 +131,7 @@ function doaddrmgt($data, $user)
if (!nuem($addr) && !nuem($ratio)) if (!nuem($addr) && !nuem($ratio))
$addrarr[] = array('addr' => $addr, 'ratio' => $ratio); $addrarr[] = array('addr' => $addr, 'ratio' => $ratio);
} }
$ans = userSettings($user, null, $addrarr, $pass); $ans = userSettings($user, null, $addrarr, $pass, $twofa);
if ($ans['STATUS'] != 'ok') if ($ans['STATUS'] != 'ok')
$err = $ans['ERROR']; $err = $ans['ERROR'];
} }

7
pool/page_allwork.php

@ -10,6 +10,7 @@ function doallwork($data, $user)
$totshare = 0; $totshare = 0;
$totdiff = 0; $totdiff = 0;
$totshrate = 0;
$totinvalid = 0; $totinvalid = 0;
$totrate = 0; $totrate = 0;
$offset = 0; $offset = 0;
@ -26,13 +27,13 @@ function doallwork($data, $user)
{ {
$pg .= workuser($data, $ans['username:'.$i], $pg .= workuser($data, $ans['username:'.$i],
$offset, $totshare, $totdiff, $offset, $totshare, $totdiff,
$totinvalid, $totrate, $totshrate, $totinvalid, $totrate,
$blockacc, $blockreward, 3600); $blockacc, $blockreward, 3600);
} }
} }
$pg .= worktotal($offset, $totshare, $totdiff, $totinvalid, $totrate, $pg .= worktotal($offset, $totshare, $totdiff, $totshrate, $totinvalid,
$blockacc, $blockreward); $totrate, $blockacc, $blockreward);
$pg .= "</table>\n"; $pg .= "</table>\n";

4
pool/page_blocks.php

@ -204,7 +204,7 @@ function doblocks($data, $user)
{ {
$pct = 100.0 * $diffratio; $pct = 100.0 * $diffratio;
list($fg, $bg) = pctcolour($pct); list($fg, $bg) = pctcolour($pct);
$bpct = "<font color=$fg>".number_format($pct, 2).'%</font>'; $bpct = "<font color=$fg>$approx".number_format($pct, 3).'%</font>';
$bg = " bgcolor=$bg"; $bg = " bgcolor=$bg";
$blktot += $diffacc; $blktot += $diffacc;
if ($stat != 'Orphan') if ($stat != 'Orphan')
@ -230,7 +230,7 @@ function doblocks($data, $user)
$pg .= "<td class=dl$ex>".utcd($ans['firstcreatedate:'.$i]).'</td>'; $pg .= "<td class=dl$ex>".utcd($ans['firstcreatedate:'.$i]).'</td>';
$pg .= "<td class=dr$ex>$stat</td>"; $pg .= "<td class=dr$ex>$stat</td>";
$pg .= "<td class=dr>$stara$approx$acc</td>"; $pg .= "<td class=dr>$stara$approx$acc</td>";
$pg .= "<td class=dr$bg>$approx$bpct</td>"; $pg .= "<td class=dr$bg>$bpct</td>";
$pg .= "<td class=dr>$cdfdsp</td>"; $pg .= "<td class=dr>$cdfdsp</td>";
$pg .= "</tr>\n"; $pg .= "</tr>\n";
} }

68
pool/page_payout.php

@ -2,20 +2,60 @@
# #
function dopayout($data, $user) function dopayout($data, $user)
{ {
$pg = '<h1>Payouts</h1>'; $t = '<span class=nn>5</span>';
$pg .= '<table width=75% cellpadding=0 cellspacing=0 border=0>'; $ot = '<span class=nn>1/5</span>';
$pg .= '<tr><td class=dc>'; $n = '<span class=nn>5Nd</span>';
$pg .= 'We use PPLNS (pay per last N shares)<br><br>'; $n1 = '<span class=nn>N</span>';
$pg .= 'The N value used for PPLNS is 5 times the network difficulty'; $n1d = '<span class=nn>Nd</span>';
$pg .= ' when a block is found,<br>'; $bc = '+101 Confirms';
$pg .= 'but includes the full shift at the start and end of the range,<br>'; $bm = 'Matured';
$pg .= 'so it usually will be a bit more than 5N.<br><br>'; $nd = 0;
$pg .= 'Shifts are ~50min long, however, when we find any pool blocks<br>'; if (isset($data['info']['currndiff']))
$pg .= 'the current shift ends at the point the block was found.<br>'; $nd = $data['info']['currndiff'];
$pg .= 'A ckpool restart will also start a new shift.<br><br>'; $nv = number_format($nd, 1);
$pg .= 'Transaction fees are included in the miner payout.<br>'; $nv5 = number_format(5.0*$nd, 1);
$pg .= 'Pool fee is 0.9% of the total.<br>';
$pg .= '</td></tr></table>'; $pg = "<h1>Payouts</h1>
<table width=75% cellpadding=0 cellspacing=0 border=0><tr><td>
<span class=hdr>What payout method does the pool use?</span><br><br>
We use <b>PPL${n1}S</b> (<b>P</b>ay <b>P</b>er <b>L</b>ast $n1 <b>S</b>hares)<br><br>
<b>PPL${n1}S</b> means that when a block is found, the block reward is shared among the last $n1 shares that miners sent to the pool, up to when the block was found.<br>
The $n1 value the pool uses is $t times the network difficulty when the block is found - '$n'.<br><br>
<span class=hdr>How much of each block does the pool reward?</span><br><br>
Transaction fees are included in the miner reward.<br>
Pool fee is 0.9% of the total.<br><br>
<span class=hdr>How do the payments work?</span><br><br>
The $n means the pool rewards $t times the expected number of shares, each time a block is found.<br>
So each share will be paid appoximately $ot of it's expected value, in each block it gets a reward,<br>
but each share is also expected, on average, to be rewarded $t times in blocks found after the share is submitted to the pool.<br>
i.e. if pool luck was always 100% then each share is expected to be rewarded $t times.<br><br>
If pool luck is better than 100%, then the average share reward will be better than $t times.<br>
If pool luck is lower than 100%, then the average share reward will be less than $t times.<br><br>
<span class=hdr>What's a shift?</span></br><br>
When your miner sends shares to the pool, the shares are not stored individually, but rather summarised into shifts.<br>
Shifts are ~50min or less in length.<br>
Aproximately every 30s, the pool generates new work and sends that to all the miners.<br>
The pool also sends new work every time a block is found on the Bitcoin network.<br>
A shift summarises all the shares submitted to the pool for 100 work changes.<br>
However, when we find pool blocks, the current shift ends at the work in which the block was found.<br>
A ckpool restart will also end the current shift and start a new one.<br><br>
<span class=hdr>So, what's the $n value?</span><br><br>
The current Bitcoin network value for $n1d is $nv and thus $n is <b>$nv5</b><br>
Bitcoin adjusts the $n1d value every 2016 blocks, which is about every 2 weeks.<br><br>
When a block is found, the reward process counts back shifts until the total share difficulty included is $n.<br>
Since shares are summarised into shifts, it will include the full shift at the end of the range counting backwards,<br>
so it usually will be a bit more than $n.<br><br>
<span class=hdr>When are payments sent out?</span><br><br>
The block 'Status' must first reach '$bc' on the Blocks page, and then is flagged as '$bm', before the reward is distributed.<br>
The block reward is sent out manually soon after that.<br><br>
</td></tr></table>";
return $pg; return $pg;
} }
# #

50
pool/page_reg.php

@ -15,7 +15,30 @@ function doregres($data, $u)
else else
$mail = ''; $mail = '';
$pg = '<br><br><table cellpadding=5 cellspacing=0 border=1><tr><td class=dc>'; $pg = '<br><br><h1>Choose one:</h1>';
$pg .= '<table cellpadding=5 cellspacing=0 border=1><tr><td class=dc>';
$pg .= '<h1>Login</h1>';
if (isset($data['data']['error']))
$pg .= "<br><b>".$data['data']['error']." - please try again</b><br><br>";
$pg .= makeForm('');
$pg .= "
<table>
<tr><td class=dr>Username:</td>
<td class=dl><input name=User value=''></td></tr>
<tr><td class=dr>Password:</td>
<td class=dl><input type=password name=Pass value=''></td></tr>
<tr><td class=dr><span class=st1>*</span>2nd Authentication:</td>
<td class=dl><input type=password name=2fa size=10></td></tr>
<tr><td colspan=2 class=dc><font size=-1><span class=st1>*</span>
Leave blank if you haven't enabled it</font></td></tr>
<tr><td>&nbsp;</td>
<td class=dl><input type=submit name=Login value=Login></td></tr>
</table>
</form>";
$pg.= '</td></tr><tr><td class=dc>';
$pg .= '<h1>Register</h1>'; $pg .= '<h1>Register</h1>';
if (isset($data['data']['error'])) if (isset($data['data']['error']))
@ -28,14 +51,14 @@ function doregres($data, $u)
<tr><td class=dr>Email:</td> <tr><td class=dr>Email:</td>
<td class=dl><input name=mail value=\"$mail\"></td></tr> <td class=dl><input name=mail value=\"$mail\"></td></tr>
<tr><td class=dr>Password:</td> <tr><td class=dr>Password:</td>
<td class=dl><input type=password name=pass></td></tr> <td class=dl><input type=password name=pass value=''></td></tr>
<tr><td class=dr>Retype Password:</td> <tr><td class=dr>Retype Password:</td>
<td class=dl><input type=password name=pass2></td></tr> <td class=dl><input type=password name=pass2 value=''></td></tr>
<tr><td>&nbsp;</td> <tr><td>&nbsp;</td>
<td class=dl><input type=submit name=Register value=Register></td></tr> <td class=dl><input type=submit name=Register value=Register></td></tr>
<tr><td colspan=2 class=dc><br><font size=-1><span class=st1>*</span> <tr><td colspan=2 class=dc><br><font size=-1><span class=st1>*</span>
All fields are required</font></td></tr> All fields are required<br>Your Username can't be a BTC address</font></td></tr>
<tr><td colspan=2 class=dc><br>". passrequires() . "</font></td></tr> <tr><td colspan=2 class=dc><br>". passrequires() . "</td></tr>
</table> </table>
</form>"; </form>";
@ -51,10 +74,9 @@ function doregres($data, $u)
<td class=dl><input name=mail value=''></td></tr> <td class=dl><input name=mail value=''></td></tr>
<tr><td>&nbsp;</td> <tr><td>&nbsp;</td>
<td class=dl><input type=submit name=Reset value=Reset></td></tr> <td class=dl><input type=submit name=Reset value=Reset></td></tr>
<tr><td colspan=2 class=dc><br><font size=-1><span class=st1>*</span>
All fields are required</font></td></tr>
<tr><td colspan=2 class=dc><br><font size=-1> <tr><td colspan=2 class=dc><br><font size=-1>
An Email will be sent to you, to let you reset your password</font></td></tr> If you enter the details correctly,<br>
an Email will be sent to you to let you reset your password</font></td></tr>
</table> </table>
</form>"; </form>";
@ -103,6 +125,12 @@ function try_reg($info, $page, $menu, $name, $u)
$ok = false; $ok = false;
else else
{ {
if (stripos($mail, 'hotmail') !== false)
{
$ok = false;
$data['error'] = "hotmail not allowed";
}
if (safepass($pass) !== true) if (safepass($pass) !== true)
{ {
$ok = false; $ok = false;
@ -184,9 +212,6 @@ function try_reset($info, $page, $menu, $name, $u)
$user = getparam('user', false); $user = getparam('user', false);
$mail = trim(getparam('mail', false)); $mail = trim(getparam('mail', false));
// Slow this right down
usleep(500000);
$data = array(); $data = array();
if (!nuem($user)) if (!nuem($user))
@ -208,6 +233,9 @@ function try_reset($info, $page, $menu, $name, $u)
# #
function show_reg($info, $page, $menu, $name, $u) function show_reg($info, $page, $menu, $name, $u)
{ {
// Slow this right down
usleep(1000000);
$reg = getparam('Register', false); $reg = getparam('Register', false);
if ($reg !== NULL) if ($reg !== NULL)
try_reg($info, $page, $menu, $name, $u); try_reg($info, $page, $menu, $name, $u);

7
pool/page_reset.php

@ -20,10 +20,12 @@ function allow_reset($error)
<td class=dl><input type=password name=pass></td></tr> <td class=dl><input type=password name=pass></td></tr>
<tr><td class=dr>Retype Password:</td> <tr><td class=dr>Retype Password:</td>
<td class=dl><input type=password name=pass2></td></tr> <td class=dl><input type=password name=pass2></td></tr>
<tr><td class=dr><span class=st1>*</span>2nd Authentication:</td>
<td class=dl><input type=password name=2fa size=10></td></tr>
<tr><td colspan=2 class=dc><br><font size=-1><span class=st1>*</span>
Leave blank if you haven't enabled it</font></td></tr>
<tr><td>&nbsp;</td> <tr><td>&nbsp;</td>
<td class=dl><input type=submit name=Update value=Update></td></tr> <td class=dl><input type=submit name=Update value=Update></td></tr>
<tr><td colspan=2 class=dc><br><font size=-1><span class=st1>*</span>
All fields are required</font></td></tr>
</table> </table>
</form>"; </form>";
@ -61,6 +63,7 @@ function dbreset()
$pass = getparam('pass', true); $pass = getparam('pass', true);
$pass2 = getparam('pass2', true); $pass2 = getparam('pass2', true);
$twofa = getparam('2fa', true);
if (nuem($pass) || nuem($pass2)) if (nuem($pass) || nuem($pass2))
return allow_reset('Enter both passwords'); return allow_reset('Enter both passwords');

45
pool/page_settings.php

@ -29,6 +29,14 @@ function settings($data, $user, $email, $addr, $err)
$pg .= '</td><td class=dl>'; $pg .= '</td><td class=dl>';
$pg .= '<input type=password name=pass size=20>'; $pg .= '<input type=password name=pass size=20>';
$pg .= '</td></tr>'; $pg .= '</td></tr>';
$pg .= '<tr class=dc><td class=dr nowrap>';
$pg .= '<span class=st1>*</span>2nd Authentication:';
$pg .= '</td><td class=dl>';
$pg .= '<input type=password name=2fa size=10>';
$pg .= '</td></tr>';
$pg .= '<tr class=dc><td colspan=2 class=dc><font size=-1>';
$pg .= "<span class=st1>*</span>Leave blank if you haven't enabled it</font>";
$pg .= '</td></tr>';
$pg .= '<tr class=dc><td class=dr colspan=2>'; $pg .= '<tr class=dc><td class=dr colspan=2>';
$pg .= 'Change: <input type=submit name=Change value=EMail>'; $pg .= 'Change: <input type=submit name=Change value=EMail>';
$pg .= '</td></tr>'; $pg .= '</td></tr>';
@ -54,6 +62,14 @@ function settings($data, $user, $email, $addr, $err)
$pg .= '</td><td class=dl>'; $pg .= '</td><td class=dl>';
$pg .= '<input type=password name=pass size=20>'; $pg .= '<input type=password name=pass size=20>';
$pg .= '</td></tr>'; $pg .= '</td></tr>';
$pg .= '<tr class=dc><td class=dr nowrap>';
$pg .= '<span class=st1>*</span>2nd Authentication:';
$pg .= '</td><td class=dl>';
$pg .= '<input type=password name=2fa size=10>';
$pg .= '</td></tr>';
$pg .= '<tr class=dc><td colspan=2 class=dc><font size=-1>';
$pg .= "<span class=st1>*</span>Leave blank if you haven't enabled it</font>";
$pg .= '</td></tr>';
$pg .= '<tr class=dc><td class=dr colspan=2>'; $pg .= '<tr class=dc><td class=dr colspan=2>';
$pg .= 'Change: <input type=submit name=Change value=Address>'; $pg .= 'Change: <input type=submit name=Change value=Address>';
$pg .= '</td></tr>'; $pg .= '</td></tr>';
@ -83,6 +99,14 @@ function settings($data, $user, $email, $addr, $err)
$pg .= '</td><td class=dl>'; $pg .= '</td><td class=dl>';
$pg .= '<input type=password name=pass2 size=20>'; $pg .= '<input type=password name=pass2 size=20>';
$pg .= '</td></tr>'; $pg .= '</td></tr>';
$pg .= '<tr class=dc><td class=dr nowrap>';
$pg .= '<span class=st1>*</span>2nd Authentication:';
$pg .= '</td><td class=dl>';
$pg .= '<input type=password name=2fa size=10>';
$pg .= '</td></tr>';
$pg .= '<tr class=dc><td colspan=2 class=dc><font size=-1>';
$pg .= "<span class=st1>*</span>Leave blank if you haven't enabled it</font>";
$pg .= '</td></tr>';
$pg .= '<tr class=dc><td class=dr colspan=2>'; $pg .= '<tr class=dc><td class=dr colspan=2>';
$pg .= 'Change: <input type=submit name=Change value=Password>'; $pg .= 'Change: <input type=submit name=Change value=Password>';
$pg .= '</td></tr>'; $pg .= '</td></tr>';
@ -103,10 +127,16 @@ function dosettings($data, $user)
{ {
case 'EMail': case 'EMail':
$email = getparam('email', false); $email = getparam('email', false);
if (stripos($email, 'hotmail') !== false)
$err = 'hotmail not allowed';
else
{
$pass = getparam('pass', false); $pass = getparam('pass', false);
$ans = userSettings($user, $email, null, $pass); $twofa = getparam('2fa', false);
$ans = userSettings($user, $email, null, $pass, $twofa);
$err = 'EMail changed'; $err = 'EMail changed';
$check = true; $check = true;
}
break; break;
case 'Address': case 'Address':
if (!isset($data['info']['u_multiaddr'])) if (!isset($data['info']['u_multiaddr']))
@ -114,7 +144,8 @@ function dosettings($data, $user)
$addr = getparam('baddr', false); $addr = getparam('baddr', false);
$addrarr = array(array('addr' => $addr)); $addrarr = array(array('addr' => $addr));
$pass = getparam('pass', false); $pass = getparam('pass', false);
$ans = userSettings($user, null, $addrarr, $pass); $twofa = getparam('2fa', false);
$ans = userSettings($user, null, $addrarr, $pass, $twofa);
$err = 'Payout address changed'; $err = 'Payout address changed';
$check = true; $check = true;
} }
@ -123,16 +154,14 @@ function dosettings($data, $user)
$oldpass = getparam('oldpass', false); $oldpass = getparam('oldpass', false);
$pass1 = getparam('pass1', false); $pass1 = getparam('pass1', false);
$pass2 = getparam('pass2', false); $pass2 = getparam('pass2', false);
$twofa = getparam('2fa', false);
if (!safepass($pass1)) if (!safepass($pass1))
{ $err = 'Unsafe password. ' . passrequires();
$err = "Password is unsafe - requires 6 or more characters, including<br>" .
"at least one of each uppercase, lowercase and digits, but not Tab";
}
elseif ($pass1 != $pass2) elseif ($pass1 != $pass2)
$err = "Passwords don't match"; $err = "Passwords don't match";
else else
{ {
$ans = setPass($user, $oldpass, $pass1); $ans = setPass($user, $oldpass, $pass1, $twofa);
$err = 'Password changed'; $err = 'Password changed';
$check = true; $check = true;
} }
@ -189,7 +218,7 @@ function dosettings($data, $user)
$old = $_SESSION['old_set_email']; $old = $_SESSION['old_set_email'];
else else
$old = null; $old = null;
emailAddressChanged($email, zeip(), $emailinfo, $old); # emailAddressChanged($email, zeip(), $emailinfo, $old);
break; break;
case 'Address': case 'Address':
payoutAddressChanged($email, zeip(), $emailinfo); payoutAddressChanged($email, zeip(), $emailinfo);

65
pool/page_workers.php

@ -10,9 +10,10 @@ function worktitle($data, $user)
$pg .= "<td class=dr><span class=nb><$r id=srtlst data-sf=n2>:Last</span> Share</td>"; $pg .= "<td class=dr><span class=nb><$r id=srtlst data-sf=n2>:Last</span> Share</td>";
$pg .= '<td class=dr>Shares</td>'; $pg .= '<td class=dr>Shares</td>';
$pg .= "<td class=dr><span class=nb><$r id=srtdiff data-sf=r4>:Diff</span></td>"; $pg .= "<td class=dr><span class=nb><$r id=srtdiff data-sf=r4>:Diff</span></td>";
$pg .= '<td class=dr>Invalid</td>'; $pg .= "<td class=dr><span class=nb><$r id=srtshrate data-sf=r5>:Share Rate</span></td>";
$pg .= "<td class=dr><span class=nb><$r id=srtinv data-sf=r6>:Invalid</span></td>";
$pg .= '<td class=dr>Block %</td>'; $pg .= '<td class=dr>Block %</td>';
$pg .= "<td class=dr><span class=nb><$r id=srtrate data-sf=r7>:Hash</span> Rate</td>"; $pg .= "<td class=dr><span class=nb><$r id=srtrate data-sf=r8>:Hash</span> Rate</td>";
$pg .= "</tr>\n"; $pg .= "</tr>\n";
return $pg; return $pg;
} }
@ -23,7 +24,7 @@ function workhashorder($a, $b)
} }
# #
function workuser($data, $user, &$offset, &$totshare, &$totdiff, function workuser($data, $user, &$offset, &$totshare, &$totdiff,
&$totinvalid, &$totrate, &$blockacc, &$totshrate, &$totinvalid, &$totrate, &$blockacc,
&$blockreward, $old = false, $srt = false, &$blockreward, $old = false, $srt = false,
$one = false, &$title) $one = false, &$title)
{ {
@ -63,6 +64,8 @@ function workuser($data, $user, &$offset, &$totshare, &$totdiff,
'w_diffacc' => $ans['w_diffacc:'.$i], 'w_diffacc' => $ans['w_diffacc:'.$i],
'w_diffinv' => $ans['w_diffinv:'.$i], 'w_diffinv' => $ans['w_diffinv:'.$i],
'w_lastdiff' => $ans['w_lastdiff:'.$i], 'w_lastdiff' => $ans['w_lastdiff:'.$i],
'w_active_diffacc' => $ans['w_active_diffacc:'.$i],
'w_active_start' => $ans['w_active_start:'.$i],
'w_uhr' => $uhr); 'w_uhr' => $uhr);
} }
@ -92,25 +95,49 @@ function workuser($data, $user, &$offset, &$totshare, &$totdiff,
$shareacc = number_format($all[$i]['w_shareacc'], 0); $shareacc = number_format($all[$i]['w_shareacc'], 0);
$totshare += $all[$i]['w_shareacc']; $totshare += $all[$i]['w_shareacc'];
$diffacc = number_format($all[$i]['w_diffacc'], 0); $dacc = $all[$i]['w_diffacc'];
$ds = round($all[$i]['w_diffacc']); $diffacc = number_format($dacc, 0);
$totdiff += $all[$i]['w_diffacc']; $ds = round($dacc);
$totdiff += $dacc;
$pg .= "<td class=dr>$shareacc</td>"; $pg .= "<td class=dr>$shareacc</td>";
$pg .= "<td class=dr data-srt=$ds>$diffacc</td>"; $pg .= "<td class=dr data-srt=$ds>$diffacc</td>";
$dtot = $all[$i]['w_diffacc'] + $all[$i]['w_diffinv']; $acthr = '0';
$acthrv = 0;
$actstt = $all[$i]['w_active_start'];
if ($actstt > 0)
{
$elapsed = $ans['STAMP'] - $actstt;
if ($elapsed > 0)
{
$acthrv = $all[$i]['w_active_diffacc'] *
pow(2,32) / $elapsed;
$acthr = dsprate($acthrv);
$totshrate += $acthrv;
}
}
$pg .= "<td class=dr data-srt=$acthrv>$acthr</td>";
$dinv = $all[$i]['w_diffinv'];
$dtot = $dacc + $dinv;
if ($dtot > 0) if ($dtot > 0)
$rej = number_format(100.0 * $all[$i]['w_diffinv'] / $dtot, 3); {
$rejf = $dinv / $dtot;
$rej = number_format(100.0 * $rejf, 3);
}
else else
{
$rejf = 0;
$rej = '0'; $rej = '0';
$totinvalid += $all[$i]['w_diffinv']; }
$totinvalid += $dinv;
$pg .= "<td class=dr>$rej%</td>"; $pg .= "<td class=dr data-srt=$rejf>$rej%</td>";
if ($blockacc <= 0) if ($blockacc <= 0)
$blkpct = '&nbsp;'; $blkpct = '&nbsp;';
else else
$blkpct = number_format(100.0 * $all[$i]['w_diffacc'] / $blockacc, 3) . '%'; $blkpct = number_format(100.0 * $dacc / $blockacc, 3) . '%';
$pg .= "<td class=dr>$blkpct</td>"; $pg .= "<td class=dr>$blkpct</td>";
@ -136,9 +163,11 @@ function workuser($data, $user, &$offset, &$totshare, &$totdiff,
return $pg; return $pg;
} }
# #
function worktotal($offset, $totshare, $totdiff, $totinvalid, $totrate, $blockacc, $blockreward) function worktotal($offset, $totshare, $totdiff, $totshrate, $totinvalid,
$totrate, $blockacc, $blockreward)
{ {
$pg = ''; $pg = '';
$totshrate = dsprate($totshrate);
$totrate = dsprate($totrate); $totrate = dsprate($totrate);
if (($offset % 2) == 0) if (($offset % 2) == 0)
$row = 'even'; $row = 'even';
@ -149,6 +178,7 @@ function worktotal($offset, $totshare, $totdiff, $totinvalid, $totrate, $blockac
$pg .= "<td class=dr>$shareacc</td>"; $pg .= "<td class=dr>$shareacc</td>";
$diffacc = number_format($totdiff, 0); $diffacc = number_format($totdiff, 0);
$pg .= "<td class=dr>$diffacc</td>"; $pg .= "<td class=dr>$diffacc</td>";
$pg .= "<td class=dr>$totshrate</td>";
$dtot = $totdiff + $totinvalid; $dtot = $totdiff + $totinvalid;
if ($dtot > 0) if ($dtot > 0)
$rej = number_format(100.0 * $totinvalid / $dtot, 3); $rej = number_format(100.0 * $totinvalid / $dtot, 3);
@ -172,6 +202,7 @@ function doworker($data, $user)
$totshare = 0; $totshare = 0;
$totdiff = 0; $totdiff = 0;
$totshrate = 0;
$totinvalid = 0; $totinvalid = 0;
$totrate = 0; $totrate = 0;
$offset = 0; $offset = 0;
@ -179,11 +210,11 @@ function doworker($data, $user)
$blockreward = 0; $blockreward = 0;
$pg .= worktitle($data, $user); $pg .= worktitle($data, $user);
$pg .= workuser($data, $user, $offset, $totshare, $totdiff, $totinvalid, $pg .= workuser($data, $user, $offset, $totshare, $totdiff, $totshrate,
$totrate, $blockacc, $blockreward, false, true, true, $totinvalid, $totrate, $blockacc, $blockreward,
$title); false, true, true, $title);
$pg .= worktotal($offset, $totshare, $totdiff, $totinvalid, $totrate, $pg .= worktotal($offset, $totshare, $totdiff, $totshrate, $totinvalid,
$blockacc, $blockreward); $totrate, $blockacc, $blockreward);
if (false && $blockacc > 0 && $blockreward > 0) if (false && $blockacc > 0 && $blockreward > 0)
{ {

5
pool/prime.php

@ -73,7 +73,8 @@ function check()
'Rewards' => 'mpayouts', 'Rewards' => 'mpayouts',
'Payments' => 'payments', 'Payments' => 'payments',
'Settings' => 'settings', 'Settings' => 'settings',
'User Settings' => 'userset' 'User Settings' => 'userset',
'2FA Settings' => '2fa'
), ),
'Workers' => array( 'Workers' => array(
'Shifts' => 'shifts', 'Shifts' => 'shifts',
@ -105,7 +106,7 @@ function check()
showPage(NULL, 'reset', $dmenu, '', $who); showPage(NULL, 'reset', $dmenu, '', $who);
else else
{ {
if (requestRegister() == true) if (requestLoginRegReset() == true)
showPage(NULL, 'reg', $dmenu, '', $who); showPage(NULL, 'reg', $dmenu, '', $who);
else else
{ {

5
sql/ckdb.sql

@ -21,6 +21,8 @@ CREATE TABLE users (
passwordhash character varying(256) NOT NULL, passwordhash character varying(256) NOT NULL,
secondaryuserid character varying(64) NOT NULL, secondaryuserid character varying(64) NOT NULL,
salt character varying(256) DEFAULT ''::character varying NOT NULL, salt character varying(256) DEFAULT ''::character varying NOT NULL,
userdata text DEFAULT ''::text NOT NULL,
userbits bigint NOT NULL,
createdate timestamp with time zone NOT NULL, createdate timestamp with time zone NOT NULL,
createby character varying(64) DEFAULT ''::character varying NOT NULL, createby character varying(64) DEFAULT ''::character varying NOT NULL,
createcode character varying(128) DEFAULT ''::character varying NOT NULL, createcode character varying(128) DEFAULT ''::character varying NOT NULL,
@ -57,6 +59,7 @@ CREATE TABLE workers (
difficultydefault integer DEFAULT 0 NOT NULL, -- 0 means default difficultydefault integer DEFAULT 0 NOT NULL, -- 0 means default
idlenotificationenabled char DEFAULT 'n'::character varying NOT NULL, idlenotificationenabled char DEFAULT 'n'::character varying NOT NULL,
idlenotificationtime integer DEFAULT 10 NOT NULL, idlenotificationtime integer DEFAULT 10 NOT NULL,
workerbits bigint NOT NULL,
createdate timestamp with time zone NOT NULL, createdate timestamp with time zone NOT NULL,
createby character varying(64) DEFAULT ''::character varying NOT NULL, createby character varying(64) DEFAULT ''::character varying NOT NULL,
createcode character varying(128) DEFAULT ''::character varying NOT NULL, createcode character varying(128) DEFAULT ''::character varying NOT NULL,
@ -460,4 +463,4 @@ CREATE TABLE version (
PRIMARY KEY (vlock) PRIMARY KEY (vlock)
); );
insert into version (vlock,version) values (1,'1.0.0'); insert into version (vlock,version) values (1,'1.0.1');

38
sql/v1.0.0-v1.0.1.sql

@ -0,0 +1,38 @@
SET SESSION AUTHORIZATION 'postgres';
BEGIN transaction;
DO $$
DECLARE ver TEXT;
BEGIN
UPDATE version set version='1.0.1' where vlock=1 and version='1.0.0';
IF found THEN
RETURN;
END IF;
SELECT version into ver from version
WHERE vlock=1;
RAISE EXCEPTION 'Wrong DB version - expect "1.0.0" - found "%"', ver;
END $$;
ALTER TABLE ONLY users
ADD COLUMN userdata text DEFAULT ''::text NOT NULL,
ADD COLUMN userbits bigint NOT NULL DEFAULT 0;
ALTER TABLE ONLY users
ALTER COLUMN userbits DROP DEFAULT;
-- match based on ckdb_data.c like_address()
UPDATE users set userbits=1 where username ~ '[13][A-HJ-NP-Za-km-z1-9]{15,}';
ALTER TABLE ONLY workers
ADD COLUMN workerbits bigint NOT NULL DEFAULT 0;
ALTER TABLE ONLY workers
ALTER COLUMN workerbits DROP DEFAULT;
END transaction;

4
src/Makefile.am

@ -22,6 +22,6 @@ notifier_LDADD = libckpool.la @JANSSON_LIBS@
if WANT_CKDB if WANT_CKDB
bin_PROGRAMS += ckdb bin_PROGRAMS += ckdb
ckdb_SOURCES = ckdb.c ckdb_cmd.c ckdb_data.c ckdb_dbio.c ckdb_btc.c \ ckdb_SOURCES = ckdb.c ckdb_cmd.c ckdb_data.c ckdb_dbio.c ckdb_btc.c \
ckdb.h klist.c ktree.c klist.h ktree.h ckdb_crypt.c ckdb.h klist.c ktree.c klist.h ktree.h
ckdb_LDADD = libckpool.la @JANSSON_LIBS@ @PQ_LIBS@ @MATH_LIBS@ ckdb_LDADD = libckpool.la @JANSSON_LIBS@ @DB_LIBS@ @MATH_LIBS@
endif endif

2
src/ckdb.c

@ -3935,6 +3935,7 @@ static void *socketer(__maybe_unused void *arg)
fflush(global_ckp->logfp); fflush(global_ckp->logfp);
break; break;
case CMD_CHKPASS: case CMD_CHKPASS:
case CMD_2FA:
case CMD_ADDUSER: case CMD_ADDUSER:
case CMD_NEWPASS: case CMD_NEWPASS:
case CMD_USERSET: case CMD_USERSET:
@ -4262,6 +4263,7 @@ static void reload_line(PGconn *conn, char *filename, uint64_t count, char *buf)
case CMD_ADDUSER: case CMD_ADDUSER:
case CMD_NEWPASS: case CMD_NEWPASS:
case CMD_CHKPASS: case CMD_CHKPASS:
case CMD_2FA:
case CMD_USERSET: case CMD_USERSET:
case CMD_WORKERSET: case CMD_WORKERSET:
case CMD_BLOCKLIST: case CMD_BLOCKLIST:

131
src/ckdb.h

@ -54,8 +54,8 @@
*/ */
#define DB_VLOCK "1" #define DB_VLOCK "1"
#define DB_VERSION "1.0.0" #define DB_VERSION "1.0.1"
#define CKDB_VERSION DB_VERSION"-1.112" #define CKDB_VERSION DB_VERSION"-1.200"
#define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL " - from %s %s() line %d"
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__ #define WHERE_FFL_HERE __FILE__, __func__, __LINE__
@ -369,6 +369,7 @@ enum cmd_values {
CMD_HEARTBEAT, CMD_HEARTBEAT,
CMD_NEWPASS, CMD_NEWPASS,
CMD_CHKPASS, CMD_CHKPASS,
CMD_2FA,
CMD_USERSET, CMD_USERSET,
CMD_WORKERSET, CMD_WORKERSET,
CMD_POOLSTAT, CMD_POOLSTAT,
@ -1017,6 +1018,9 @@ typedef struct users {
char passwordhash[TXT_BIG+1]; char passwordhash[TXT_BIG+1];
char secondaryuserid[TXT_SML+1]; char secondaryuserid[TXT_SML+1];
char salt[TXT_BIG+1]; char salt[TXT_BIG+1];
char *userdata;
int64_t databits; // non-DB field, Bitmask of userdata content
int64_t userbits; // Bitmask of user attributes
HISTORYDATECONTROLFIELDS; HISTORYDATECONTROLFIELDS;
} USERS; } USERS;
@ -1031,6 +1035,36 @@ typedef struct users {
#define SALTSIZHEX 32 #define SALTSIZHEX 32
#define SALTSIZBIN 16 #define SALTSIZBIN 16
#define DATABITS_SEP ','
#define DATABITS_SEP_STR ","
/* databits attributes
* These are generated at dbload time from userdata
* and when the userdata is changed */
// TOTP Auth 2FA
#define USER_TOTPAUTH_NAME "totpauth"
#define USER_TOTPAUTH 0x1
// 2FA Key untested
#define USER_TEST2FA_NAME "test2fa"
#define USER_TEST2FA 0x2
#define USER_TOTP_ENA(_users) \
(((_users)->databits & (USER_TOTPAUTH | USER_TEST2FA)) == USER_TOTPAUTH)
// userbits attributes
// Address account, not a username account
#define USER_ADDRESS 0x1
// 16 x base 32 (5 bits) = 10 bytes (8 bits)
#define TOTPAUTH_KEYSIZE 10
#define TOTPAUTH_DSP_KEYSIZE 16
// Optioncontrol name
#define TOTPAUTH_ISSUER "taissuer"
// Currently only:
#define TOTPAUTH_AUTH "totp"
#define TOTPAUTH_HASH "SHA256"
#define TOTPAUTH_TIME 30
extern K_TREE *users_root; extern K_TREE *users_root;
extern K_TREE *userid_root; extern K_TREE *userid_root;
extern K_LIST *users_free; extern K_LIST *users_free;
@ -1073,6 +1107,7 @@ typedef struct workers {
int32_t difficultydefault; int32_t difficultydefault;
char idlenotificationenabled[TXT_FLAG+1]; char idlenotificationenabled[TXT_FLAG+1];
int32_t idlenotificationtime; int32_t idlenotificationtime;
int64_t workerbits; // Bitmask of worker attributes
HISTORYDATECONTROLFIELDS; HISTORYDATECONTROLFIELDS;
} WORKERS; } WORKERS;
@ -1086,6 +1121,8 @@ extern K_TREE *workers_root;
extern K_LIST *workers_free; extern K_LIST *workers_free;
extern K_STORE *workers_store; extern K_STORE *workers_store;
// Currently no workerbits attributes
#define DIFFICULTYDEFAULT_MIN 10 #define DIFFICULTYDEFAULT_MIN 10
#define DIFFICULTYDEFAULT_MAX 0x7fffffff #define DIFFICULTYDEFAULT_MAX 0x7fffffff
// 0 means it's not set // 0 means it's not set
@ -1735,18 +1772,32 @@ typedef struct workerstatus {
tv_t last_stats; tv_t last_stats;
tv_t last_idle; tv_t last_idle;
// Below gets reset on each block // Below gets reset on each block
double diffacc; double block_diffacc;
double diffinv; // Non-acc double block_diffinv; // Non-acc
double diffsta; double block_diffsta;
double diffdup; double block_diffdup;
double diffhi; double block_diffhi;
double diffrej; double block_diffrej;
double shareacc; double block_shareacc;
double shareinv; // Non-acc double block_shareinv; // Non-acc
double sharesta; double block_sharesta;
double sharedup; double block_sharedup;
double sharehi; double block_sharehi;
double sharerej; double block_sharerej;
// Below gets reset on each idle
double active_diffacc;
double active_diffinv; // Non-acc
double active_diffsta;
double active_diffdup;
double active_diffhi;
double active_diffrej;
double active_shareacc;
double active_shareinv; // Non-acc
double active_sharesta;
double active_sharedup;
double active_sharehi;
double active_sharerej;
tv_t active_start;
} WORKERSTATUS; } WORKERSTATUS;
#define ALLOC_WORKERSTATUS 1000 #define ALLOC_WORKERSTATUS 1000
@ -2086,8 +2137,35 @@ extern cmp_t cmp_userid(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_users(char *username); extern K_ITEM *find_users(char *username);
extern K_ITEM *find_userid(int64_t userid); extern K_ITEM *find_userid(int64_t userid);
extern void make_salt(USERS *users); extern void make_salt(USERS *users);
extern void password_hash(char *username, char *passwordhash, char *salt, char *result, size_t siz); extern void password_hash(char *username, char *passwordhash, char *salt,
char *result, size_t siz);
extern bool check_hash(USERS *users, char *passwordhash); extern bool check_hash(USERS *users, char *passwordhash);
extern void users_databits(USERS *users);
#define users_userdata_get_hex(_users, _name, _bit, _hexlen) \
_users_userdata_get_hex(_users, _name, _bit, _hexlen, WHERE_FFL_HERE)
extern char *_users_userdata_get_hex(USERS *users, char *name, int64_t bit,
size_t *hexlen, WHERE_FFL_ARGS);
#define users_userdata_get_bin(_users, _name, _bit, _binlen) \
_users_userdata_get_bin(_users, _name, _bit, _binlen, WHERE_FFL_HERE)
extern unsigned char *_users_userdata_get_bin(USERS *users, char *name,
int64_t bit, size_t *binlen,
WHERE_FFL_ARGS);
#define users_userdata_del(_users, _name, _bit) \
_users_userdata_del(_users, _name, _bit, WHERE_FFL_HERE)
extern void _users_userdata_del(USERS *users, char *name, int64_t bit,
WHERE_FFL_ARGS);
// If we want to store a simple string, no point encoding it
#define users_userdata_add_txt(_users, _name, _bit, _hex) \
_users_userdata_add_hex(_users, _name, _bit, _hex, WHERE_FFL_HERE)
#define users_userdata_add_hex(_users, _name, _bit, _hex) \
_users_userdata_add_hex(_users, _name, _bit, _hex, WHERE_FFL_HERE)
extern void _users_userdata_add_hex(USERS *users, char *name, int64_t bit,
char *hex, WHERE_FFL_ARGS);
#define users_userdata_add_bin(_users, _name, _bit, _bin, _len) \
_users_userdata_add_bin(_users, _name, _bit, _bin, _len, WHERE_FFL_HERE)
extern void _users_userdata_add_bin(USERS *users, char *name, int64_t bit,
unsigned char *bin, size_t len,
WHERE_FFL_ARGS);
extern cmp_t cmp_useratts(K_ITEM *a, K_ITEM *b); extern cmp_t cmp_useratts(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_useratts(int64_t userid, char *attname); extern K_ITEM *find_useratts(int64_t userid, char *attname);
extern cmp_t cmp_workers(K_ITEM *a, K_ITEM *b); extern cmp_t cmp_workers(K_ITEM *a, K_ITEM *b);
@ -2285,8 +2363,11 @@ extern bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash,
char *newhash, char *email, char *by, char *code, char *newhash, char *email, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root, char *status); char *inet, tv_t *cd, K_TREE *trf_root, char *status);
extern K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress, extern K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
char *passwordhash, char *by, char *code, char *inet, char *passwordhash, int64_t userbits, char *by,
tv_t *cd, K_TREE *trf_root); char *code, char *inet, tv_t *cd, K_TREE *trf_root);
extern bool users_replace(PGconn *conn, K_ITEM *u_item, K_ITEM *old_u_item,
char *by, char *code, char *inet, tv_t *cd,
K_TREE *trf_root);
extern bool users_fill(PGconn *conn); extern bool users_fill(PGconn *conn);
extern bool useratts_item_add(PGconn *conn, K_ITEM *ua_item, tv_t *cd, bool begun); extern bool useratts_item_add(PGconn *conn, K_ITEM *ua_item, tv_t *cd, bool begun);
extern K_ITEM *useratts_add(PGconn *conn, char *username, char *attname, extern K_ITEM *useratts_add(PGconn *conn, char *username, char *attname,
@ -2455,4 +2536,20 @@ extern struct CMDS ckdb_cmds[];
extern bool btc_valid_address(char *addr); extern bool btc_valid_address(char *addr);
extern void btc_blockstatus(BLOCKS *blocks); extern void btc_blockstatus(BLOCKS *blocks);
// ***
// *** ckdb_crypt.c
// ***
#define tob32(_users, _bin, _len, _name, _olen) \
_tob32(_users, _bin, _len, _name, _olen, WHERE_FFL_HERE)
extern char *_tob32(USERS *users, unsigned char *bin, size_t len, char *name,
size_t olen, WHERE_FFL_ARGS);
extern bool gen_data(USERS *users, unsigned char *buf, size_t len,
int32_t entropy);
extern K_ITEM *gen_2fa_key(K_ITEM *old_u_item, int32_t entropy, char *by,
char *code, char *inet, tv_t *cd, K_TREE *trf_root);
extern bool check_2fa(USERS *users, int32_t value);
extern bool tst_2fa(K_ITEM *old_u_item, int32_t value, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root);
#endif #endif

330
src/ckdb_cmd.c

@ -67,7 +67,7 @@ static char *cmd_adduser(PGconn *conn, char *cmd, char *id, tv_t *now, char *by,
u_item = users_add(conn, transfer_data(i_username), u_item = users_add(conn, transfer_data(i_username),
transfer_data(i_emailaddress), transfer_data(i_emailaddress),
transfer_data(i_passwordhash), transfer_data(i_passwordhash), 0,
by, code, inet, now, trf_root); by, code, inet, now, trf_root);
} }
@ -84,11 +84,13 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
tv_t *now, char *by, char *code, char *inet, tv_t *now, char *by, char *code, char *inet,
__maybe_unused tv_t *cd, K_TREE *trf_root) __maybe_unused tv_t *cd, K_TREE *trf_root)
{ {
K_ITEM *i_username, *i_oldhash, *i_newhash, *u_item; K_ITEM *i_username, *i_oldhash, *i_newhash, *i_2fa, *u_item;
char reply[1024] = ""; char reply[1024] = "";
size_t siz = sizeof(reply); size_t siz = sizeof(reply);
bool ok = true; bool ok = true;
char *oldhash; char *oldhash;
int32_t value;
USERS *users;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd); LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
@ -108,6 +110,10 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
oldhash = EMPTY; oldhash = EMPTY;
} }
i_2fa = require_name(trf_root, "2fa", 1, (char *)intpatt, reply, siz);
if (!i_2fa)
return strdup(reply);
if (ok) { if (ok) {
i_newhash = require_name(trf_root, "newhash", i_newhash = require_name(trf_root, "newhash",
64, (char *)hashpatt, 64, (char *)hashpatt,
@ -120,13 +126,21 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
K_RUNLOCK(users_free); K_RUNLOCK(users_free);
if (u_item) { if (u_item) {
ok = users_update(NULL, u_item, DATA_USERS(users, u_item);
if (USER_TOTP_ENA(users)) {
value = (int32_t)atoi(transfer_data(i_2fa));
ok = check_2fa(users, value);
}
if (ok) {
ok = users_update(NULL,
u_item,
oldhash, oldhash,
transfer_data(i_newhash), transfer_data(i_newhash),
NULL, NULL,
by, code, inet, now, by, code, inet, now,
trf_root, trf_root,
NULL); NULL);
}
} else } else
ok = false; ok = false;
} }
@ -144,7 +158,7 @@ static char *cmd_chkpass(__maybe_unused PGconn *conn, char *cmd, char *id,
__maybe_unused char *code, __maybe_unused char *inet, __maybe_unused char *code, __maybe_unused char *inet,
__maybe_unused tv_t *notcd, K_TREE *trf_root) __maybe_unused tv_t *notcd, K_TREE *trf_root)
{ {
K_ITEM *i_username, *i_passwordhash, *u_item; K_ITEM *i_username, *i_passwordhash, *i_2fa, *u_item;
char reply[1024] = ""; char reply[1024] = "";
size_t siz = sizeof(reply); size_t siz = sizeof(reply);
USERS *users; USERS *users;
@ -160,6 +174,10 @@ static char *cmd_chkpass(__maybe_unused PGconn *conn, char *cmd, char *id,
if (!i_passwordhash) if (!i_passwordhash)
return strdup(reply); return strdup(reply);
i_2fa = require_name(trf_root, "2fa", 1, (char *)intpatt, reply, siz);
if (!i_2fa)
return strdup(reply);
K_RLOCK(users_free); K_RLOCK(users_free);
u_item = find_users(transfer_data(i_username)); u_item = find_users(transfer_data(i_username));
K_RUNLOCK(users_free); K_RUNLOCK(users_free);
@ -169,6 +187,10 @@ static char *cmd_chkpass(__maybe_unused PGconn *conn, char *cmd, char *id,
else { else {
DATA_USERS(users, u_item); DATA_USERS(users, u_item);
ok = check_hash(users, transfer_data(i_passwordhash)); ok = check_hash(users, transfer_data(i_passwordhash));
if (ok && USER_TOTP_ENA(users)) {
uint32_t value = (int32_t)atoi(transfer_data(i_2fa));
ok = check_2fa(users, value);
}
} }
if (!ok) { if (!ok) {
@ -179,13 +201,212 @@ static char *cmd_chkpass(__maybe_unused PGconn *conn, char *cmd, char *id,
return strdup("ok."); return strdup("ok.");
} }
static char *cmd_2fa(__maybe_unused PGconn *conn, char *cmd, char *id,
tv_t *now, char *by, char *code, char *inet,
__maybe_unused tv_t *notcd, K_TREE *trf_root)
{
K_ITEM *i_username, *i_action, *i_entropy, *i_value, *u_item, *u_new;
char reply[1024] = "";
size_t siz = sizeof(reply);
size_t len, off;
char tmp[1024];
int32_t entropy, value;
USERS *users;
char *action, *buf = NULL, *st = NULL;
char *sfa_status = EMPTY, *sfa_error = EMPTY;
bool ok = false, key = false;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
i_username = require_name(trf_root, "username", 3, (char *)userpatt,
reply, siz);
if (!i_username)
return strdup(reply);
// Field always expected, blank means to report the status
i_action = require_name(trf_root, "action", 0, NULL, reply, siz);
if (!i_action)
return strdup(reply);
action = transfer_data(i_action);
/* Field always expected with a value,
* but the value is only used when generating a Secret Key */
i_entropy = require_name(trf_root, "entropy", 1, (char *)intpatt,
reply, siz);
if (!i_entropy)
return strdup(reply);
// Field always expected, use 0 if not required
i_value = require_name(trf_root, "value", 1, (char *)intpatt,
reply, siz);
if (!i_value)
return strdup(reply);
K_RLOCK(users_free);
u_item = find_users(transfer_data(i_username));
K_RUNLOCK(users_free);
if (u_item) {
DATA_USERS(users, u_item);
APPEND_REALLOC_INIT(buf, off, len);
APPEND_REALLOC(buf, off, len, "ok.");
switch (users->databits & (USER_TOTPAUTH | USER_TEST2FA)) {
case 0:
break;
case USER_TOTPAUTH:
sfa_status = "ok";
break;
case (USER_TOTPAUTH | USER_TEST2FA):
sfa_status = "test";
key = true;
break;
default:
// USER_TEST2FA only <- currently invalid
LOGERR("%s() users databits invalid for "
"'%s/%"PRId64,
__func__,
st = safe_text_nonull(users->username),
users->databits);
goto dame;
}
if (!*action) {
ok = true;
} else if (strcmp(action, "setup") == 0) {
// Can't setup if anything is already present -> new
if (users->databits & (USER_TOTPAUTH | USER_TEST2FA))
goto dame;
entropy = (int32_t)atoi(transfer_data(i_entropy));
u_new = gen_2fa_key(u_item, entropy, by, code, inet,
now, trf_root);
if (u_new) {
ok = true;
sfa_status = "test";
key = true;
u_item = u_new;
DATA_USERS(users, u_item);
}
} else if (strcmp(action, "test") == 0) {
// Can't test if it's not ready to test
if ((users->databits & (USER_TOTPAUTH | USER_TEST2FA))
!= (USER_TOTPAUTH | USER_TEST2FA))
goto dame;
value = (int32_t)atoi(transfer_data(i_value));
ok = tst_2fa(u_item, value, by, code, inet, now,
trf_root);
if (!ok)
sfa_error = "Invalid code";
else {
key = false;
sfa_status = "ok";
}
// Report sfa_error to web
ok = true;
} else if (strcmp(action, "new") == 0) {
// Can't new if 2FA isn't already present -> setup
if ((users->databits & USER_TOTPAUTH) == 0)
goto dame;
value = (int32_t)atoi(transfer_data(i_value));
if (!check_2fa(users, value)) {
sfa_error = "Invalid code";
// Report sfa_error to web
ok = true;
} else {
entropy = (int32_t)atoi(transfer_data(i_entropy));
u_new = gen_2fa_key(u_item, entropy, by, code,
inet, now, trf_root);
if (u_new) {
ok = true;
sfa_status = "test";
key = true;
u_item = u_new;
DATA_USERS(users, u_item);
}
}
}
if (key) {
char *keystr, *issuer = "Kano";
char cd_buf[DATE_BUFSIZ];
unsigned char *bin;
OPTIONCONTROL *oc;
K_ITEM *oc_item;
size_t binlen;
bin = users_userdata_get_bin(users,
USER_TOTPAUTH_NAME,
USER_TOTPAUTH,
&binlen);
if (binlen != TOTPAUTH_KEYSIZE) {
LOGERR("%s() invalid key for '%s/%s "
"len(%d) != %d",
__func__,
st = safe_text_nonull(users->username),
USER_TOTPAUTH_NAME, (int)binlen,
TOTPAUTH_KEYSIZE);
FREENULL(st);
}
if (bin && binlen == TOTPAUTH_KEYSIZE) {
keystr = tob32(users, bin, binlen,
USER_TOTPAUTH_NAME,
TOTPAUTH_DSP_KEYSIZE);
snprintf(tmp, sizeof(tmp), "2fa_key=%s%c",
keystr, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
FREENULL(keystr);
K_RLOCK(optioncontrol_free);
oc_item = find_optioncontrol(TOTPAUTH_ISSUER,
now,
OPTIONCONTROL_HEIGHT);
K_RUNLOCK(optioncontrol_free);
if (oc_item) {
DATA_OPTIONCONTROL(oc, oc_item);
issuer = oc->optionvalue;
} else {
tv_to_buf(now, cd_buf, sizeof(cd_buf));
LOGEMERG("%s(): missing optioncontrol "
"%s (%s/%d)",
__func__, TOTPAUTH_ISSUER,
cd_buf, OPTIONCONTROL_HEIGHT);
}
// TODO: add issuer to optioncontrol
snprintf(tmp, sizeof(tmp),
"2fa_auth=%s%c2fa_hash=%s%c"
"2fa_time=%d%c2fa_issuer=%s%c",
TOTPAUTH_AUTH, FLDSEP,
TOTPAUTH_HASH, FLDSEP,
TOTPAUTH_TIME, FLDSEP,
issuer, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
}
FREENULL(bin);
}
}
if (!ok) {
dame:
// Only db/php/code errors should get here
LOGERR("%s.failed.%s-%s", id, transfer_data(i_username), action);
FREENULL(buf);
return strdup("failed.");
}
snprintf(tmp, sizeof(tmp), "2fa_status=%s%c2fa_error=%s",
sfa_status, FLDSEP, sfa_error);
APPEND_REALLOC(buf, off, len, tmp);
LOGDEBUG("%s.%s-%s.%s", id, transfer_data(i_username), action, buf);
return buf;
}
static char *cmd_userset(PGconn *conn, char *cmd, char *id, static char *cmd_userset(PGconn *conn, char *cmd, char *id,
__maybe_unused tv_t *now, __maybe_unused char *by, __maybe_unused tv_t *now, __maybe_unused char *by,
__maybe_unused char *code, __maybe_unused char *inet, __maybe_unused char *code, __maybe_unused char *inet,
__maybe_unused tv_t *notcd, K_TREE *trf_root) __maybe_unused tv_t *notcd, K_TREE *trf_root)
{ {
K_ITEM *i_username, *i_passwordhash, *i_rows, *i_address, *i_ratio; K_ITEM *i_username, *i_passwordhash, *i_2fa, *i_rows, *i_address;
K_ITEM *i_email, *u_item, *pa_item, *old_pa_item; K_ITEM *i_ratio, *i_email, *u_item, *pa_item, *old_pa_item;
char *email, *address; char *email, *address;
char reply[1024] = ""; char reply[1024] = "";
size_t siz = sizeof(reply); size_t siz = sizeof(reply);
@ -263,10 +484,26 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
"PaymentAddresses", FLDSEP, ""); "PaymentAddresses", FLDSEP, "");
APPEND_REALLOC(answer, off, len, tmp); APPEND_REALLOC(answer, off, len, tmp);
} else { } else {
i_2fa = require_name(trf_root, "2fa", 1, (char *)intpatt,
reply, siz);
if (!i_2fa) {
reason = "Invalid data";
goto struckout;
}
if (!check_hash(users, transfer_data(i_passwordhash))) { if (!check_hash(users, transfer_data(i_passwordhash))) {
reason = "Incorrect password"; reason = "Incorrect password";
goto struckout; goto struckout;
} }
if (USER_TOTP_ENA(users)) {
uint32_t value = (int32_t)atoi(transfer_data(i_2fa));
if (!check_2fa(users, value)) {
reason = "Invalid data";
goto struckout;
}
}
i_email = optional_name(trf_root, "email", i_email = optional_name(trf_root, "email",
1, (char *)mailpatt, 1, (char *)mailpatt,
reply, siz); reply, siz);
@ -1395,18 +1632,18 @@ static char *cmd_percent(char *cmd, char *id, tv_t *now, USERS *users)
ws_item = get_workerstatus(users->userid, workers->workername); ws_item = get_workerstatus(users->userid, workers->workername);
if (ws_item) { if (ws_item) {
DATA_WORKERSTATUS(workerstatus, ws_item); DATA_WORKERSTATUS(workerstatus, ws_item);
t_diffacc += workerstatus->diffacc; t_diffacc += workerstatus->block_diffacc;
t_diffinv += workerstatus->diffinv; t_diffinv += workerstatus->block_diffinv;
t_diffsta += workerstatus->diffsta; t_diffsta += workerstatus->block_diffsta;
t_diffdup += workerstatus->diffdup; t_diffdup += workerstatus->block_diffdup;
t_diffhi += workerstatus->diffhi; t_diffhi += workerstatus->block_diffhi;
t_diffrej += workerstatus->diffrej; t_diffrej += workerstatus->block_diffrej;
t_shareacc += workerstatus->shareacc; t_shareacc += workerstatus->block_shareacc;
t_shareinv += workerstatus->shareinv; t_shareinv += workerstatus->block_shareinv;
t_sharesta += workerstatus->sharesta; t_sharesta += workerstatus->block_sharesta;
t_sharedup += workerstatus->sharedup; t_sharedup += workerstatus->block_sharedup;
t_sharehi += workerstatus->sharehi; t_sharehi += workerstatus->block_sharehi;
t_sharerej += workerstatus->sharerej; t_sharerej += workerstatus->block_sharerej;
} }
/* TODO: workers_root userid+worker is ordered /* TODO: workers_root userid+worker is ordered
@ -1692,6 +1929,8 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id,
double w_shareacc, w_shareinv; double w_shareacc, w_shareinv;
double w_sharesta, w_sharedup; double w_sharesta, w_sharedup;
double w_sharehi, w_sharerej; double w_sharehi, w_sharerej;
double w_active_diffacc;
tv_t w_active_start;
w_hashrate5m = w_hashrate1hr = w_hashrate5m = w_hashrate1hr =
w_hashrate24hr = 0.0; w_hashrate24hr = 0.0;
@ -1699,30 +1938,34 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id,
if (!ws_item) { if (!ws_item) {
w_lastshare.tv_sec = 0; w_lastshare.tv_sec = 0;
w_lastdiff = w_diffacc = w_diffinv = w_lastdiff = w_diffacc =
w_diffsta = w_diffdup = w_diffinv = w_diffsta =
w_diffhi = w_diffrej = w_diffdup = w_diffhi =
w_shareacc = w_shareinv = w_diffrej = w_shareacc =
w_sharesta = w_sharedup = w_shareinv = w_sharesta =
w_sharehi = w_sharerej = 0; w_sharedup = w_sharehi =
w_sharerej = w_active_diffacc = 0;
w_active_start.tv_sec = 0;
} else { } else {
DATA_WORKERSTATUS(workerstatus, ws_item); DATA_WORKERSTATUS(workerstatus, ws_item);
// It's bad to read possibly changing data // It's bad to read possibly changing data
K_RLOCK(workerstatus_free); K_RLOCK(workerstatus_free);
w_lastshare.tv_sec = workerstatus->last_share.tv_sec; w_lastshare.tv_sec = workerstatus->last_share.tv_sec;
w_lastdiff = workerstatus->last_diff; w_lastdiff = workerstatus->last_diff;
w_diffacc = workerstatus->diffacc; w_diffacc = workerstatus->block_diffacc;
w_diffinv = workerstatus->diffinv; w_diffinv = workerstatus->block_diffinv;
w_diffsta = workerstatus->diffsta; w_diffsta = workerstatus->block_diffsta;
w_diffdup = workerstatus->diffdup; w_diffdup = workerstatus->block_diffdup;
w_diffhi = workerstatus->diffhi; w_diffhi = workerstatus->block_diffhi;
w_diffrej = workerstatus->diffrej; w_diffrej = workerstatus->block_diffrej;
w_shareacc = workerstatus->shareacc; w_shareacc = workerstatus->block_shareacc;
w_shareinv = workerstatus->shareinv; w_shareinv = workerstatus->block_shareinv;
w_sharesta = workerstatus->sharesta; w_sharesta = workerstatus->block_sharesta;
w_sharedup = workerstatus->sharedup; w_sharedup = workerstatus->block_sharedup;
w_sharehi = workerstatus->sharehi; w_sharehi = workerstatus->block_sharehi;
w_sharerej = workerstatus->sharerej; w_sharerej = workerstatus->block_sharerej;
w_active_diffacc = workerstatus->active_diffacc;
w_active_start.tv_sec = workerstatus->active_start.tv_sec;
K_RUNLOCK(workerstatus_free); K_RUNLOCK(workerstatus_free);
} }
@ -1815,6 +2058,15 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id,
double_to_buf(w_sharerej, reply, sizeof(reply)); double_to_buf(w_sharerej, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "w_sharerej:%d=%s%c", rows, reply, FLDSEP); snprintf(tmp, sizeof(tmp), "w_sharerej:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp); APPEND_REALLOC(buf, off, len, tmp);
double_to_buf(w_active_diffacc, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "w_active_diffacc:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
int_to_buf((int)(w_active_start.tv_sec), reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "w_active_start:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
} }
rows++; rows++;
} }
@ -1832,7 +2084,8 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id,
"w_lastdiff,w_diffacc,w_diffinv," "w_lastdiff,w_diffacc,w_diffinv,"
"w_diffsta,w_diffdup,w_diffhi,w_diffrej," "w_diffsta,w_diffdup,w_diffhi,w_diffrej,"
"w_shareacc,w_shareinv," "w_shareacc,w_shareinv,"
"w_sharesta,w_sharedup,w_sharehi,w_sharerej" : "", "w_sharesta,w_sharedup,w_sharehi,w_sharerej,"
"w_active_diffacc,w_active_start" : "",
FLDSEP); FLDSEP);
APPEND_REALLOC(buf, off, len, tmp); APPEND_REALLOC(buf, off, len, tmp);
@ -2460,7 +2713,7 @@ static char *cmd_auth_do(PGconn *conn, char *cmd, char *id, char *by,
if (!u_item) { if (!u_item) {
DATA_OPTIONCONTROL(optioncontrol, oc_item); DATA_OPTIONCONTROL(optioncontrol, oc_item);
u_item = users_add(conn, username, EMPTY, u_item = users_add(conn, username, EMPTY,
optioncontrol->optionvalue, optioncontrol->optionvalue, 0,
by, code, inet, cd, trf_root); by, code, inet, cd, trf_root);
} }
} }
@ -6046,6 +6299,7 @@ struct CMDS ckdb_cmds[] = {
{ CMD_ADDUSER, "adduser", false, false, cmd_adduser, SEQ_NONE, ACCESS_WEB }, { CMD_ADDUSER, "adduser", false, false, cmd_adduser, SEQ_NONE, ACCESS_WEB },
{ CMD_NEWPASS, "newpass", false, false, cmd_newpass, SEQ_NONE, ACCESS_WEB }, { CMD_NEWPASS, "newpass", false, false, cmd_newpass, SEQ_NONE, ACCESS_WEB },
{ CMD_CHKPASS, "chkpass", false, false, cmd_chkpass, SEQ_NONE, ACCESS_WEB }, { CMD_CHKPASS, "chkpass", false, false, cmd_chkpass, SEQ_NONE, ACCESS_WEB },
{ CMD_2FA, "2fa", false, false, cmd_2fa, SEQ_NONE, ACCESS_WEB },
{ CMD_USERSET, "usersettings", false, false, cmd_userset, SEQ_NONE, ACCESS_WEB }, { CMD_USERSET, "usersettings", false, false, cmd_userset, SEQ_NONE, ACCESS_WEB },
{ CMD_WORKERSET,"workerset", false, false, cmd_workerset, SEQ_NONE, ACCESS_WEB }, { CMD_WORKERSET,"workerset", false, false, cmd_workerset, SEQ_NONE, ACCESS_WEB },
{ CMD_POOLSTAT, "poolstats", false, true, cmd_poolstats, SEQ_POOLSTATS, ACCESS_POOL }, { CMD_POOLSTAT, "poolstats", false, true, cmd_poolstats, SEQ_POOLSTATS, ACCESS_POOL },

239
src/ckdb_crypt.c

@ -0,0 +1,239 @@
/*
* Copyright 2015 Andrew Smith
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version. See COPYING for more details.
*/
#include <openssl/x509.h>
#include <openssl/hmac.h>
#include "ckdb.h"
#if (SHA256SIZBIN != SHA256_DIGEST_LENGTH)
#error "SHA256SIZBIN must = OpenSSL SHA256_DIGEST_LENGTH"
#endif
static char b32code[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7'
};
#define ASSERT32(condition) __maybe_unused static char b32code_length_must_be_32[(condition)?1:-1]
ASSERT32(sizeof(b32code) == 32);
// bin is bigendian, return buf is bigendian
char *_tob32(USERS *users, unsigned char *bin, size_t len, char *name,
size_t olen, WHERE_FFL_ARGS)
{
size_t osiz = (len * 8 + 4) / 5;
char *buf, *ptr, *st = NULL;
int i, j, bits, ch;
if (osiz != olen) {
LOGEMERG("%s() of '%s' data for '%s' invalid olen=%d != osiz=%d"
WHERE_FFL,
__func__, name, safe_text_nonull(users->username),
(int)olen, (int)osiz, WHERE_FFL_PASS);
FREENULL(st);
olen = osiz;
}
buf = malloc(olen+1);
ptr = buf + olen;
*ptr = '\0';
i = 0;
while (--ptr >= buf) {
j = i / 8;
bits = (31 << (i % 8));
ch = (bin[len-j-1] & bits) >> (i % 8);
if (bits > 255)
ch |= (bin[len-j-2] & (bits >> 8)) << (8 - (i % 8));
// Shouldn't ever happen
if (ch < 0 || ch > 31) {
char *binstr = bin2hex(bin, len);
LOGEMERG("%s() failure of '%s' data for '%s' invalid "
"ch=%d, i=%d j=%d bits=%d bin=0x%s len=%d "
"olen=%d" WHERE_FFL,
__func__, name,
safe_text_nonull(users->username), ch, i, j,
bits, binstr, (int)len, (int)olen,
WHERE_FFL_PASS);
FREENULL(st);
FREENULL(binstr);
ch = 0;
}
*ptr = b32code[ch];
i += 5;
}
return buf;
}
bool gen_data(__maybe_unused USERS *users, unsigned char *buf, size_t len,
int32_t entropy)
{
unsigned char *ptr;
ssize_t ret, want, got;
int i;
int fil = open("/dev/random", O_RDONLY);
if (fil == -1)
return false;
want = (ssize_t)len;
got = 0;
while (got < want) {
ret = read(fil, buf+got, want-got);
if (ret < 0) {
close(fil);
return false;
}
got += ret;
}
close(fil);
ptr = (unsigned char *)&entropy;
for (i = 0; i < (int)sizeof(entropy) && (i + sizeof(entropy)) < len; i++)
buf[i+sizeof(entropy)] ^= *(ptr + i);
return true;
}
K_ITEM *gen_2fa_key(K_ITEM *old_u_item, int32_t entropy, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root)
{
unsigned char key[TOTPAUTH_KEYSIZE];
K_ITEM *u_item = NULL;
USERS *old_users, *users;
bool ok;
DATA_USERS(old_users, old_u_item);
ok = gen_data(old_users, key, sizeof(key), entropy);
if (ok) {
K_WLOCK(users_free);
u_item = k_unlink_head(users_free);
K_WUNLOCK(users_free);
DATA_USERS(users, u_item);
memcpy(users, old_users, sizeof(*users));
if (users->userdata != EMPTY) {
users->userdata = strdup(users->userdata);
if (!users->userdata)
quithere(1, "strdup OOM");
}
users_userdata_add_bin(users, USER_TOTPAUTH_NAME,
USER_TOTPAUTH, key, sizeof(key));
users_userdata_add_txt(users, USER_TEST2FA_NAME,
USER_TEST2FA, "Y");
ok = users_replace(NULL, u_item, old_u_item, by, code, inet, cd,
trf_root);
if (!ok) {
// u_item was cleaned up in user_replace()
u_item = NULL;
}
}
return u_item;
}
bool check_2fa(USERS *users, int32_t value)
{
char *st = NULL, *tmp1 = NULL, *tmp2 = NULL, *tmp3 = NULL;
unsigned char tim[sizeof(int64_t)], *bin, *hash;
unsigned int reslen;
size_t binlen;
HMAC_CTX ctx;
int64_t now;
int32_t otp;
int i, offset;
now = (int64_t)time(NULL) / TOTPAUTH_TIME;
bin = users_userdata_get_bin(users, USER_TOTPAUTH_NAME,
USER_TOTPAUTH, &binlen);
if (binlen != TOTPAUTH_KEYSIZE) {
LOGERR("%s() invalid key for '%s/%s "
"len(%d) != %d",
__func__,
st = safe_text_nonull(users->username),
USER_TOTPAUTH_NAME, (int)binlen,
TOTPAUTH_KEYSIZE);
FREENULL(st);
return false;
}
for (i = 0; i < (int)sizeof(int64_t); i++)
tim[i] = (now >> 8 * ((sizeof(int64_t) - 1) - i)) & 0xff;
LOGDEBUG("%s() '%s/%s tim=%"PRId64"=%s key=%s=%s", __func__,
st = safe_text_nonull(users->username),
USER_TOTPAUTH_NAME, now,
tmp1 = (char *)bin2hex(&tim, sizeof(tim)),
tmp2 = (char *)bin2hex(bin, TOTPAUTH_KEYSIZE),
tmp3 = tob32(users, bin, binlen,USER_TOTPAUTH_NAME, TOTPAUTH_DSP_KEYSIZE));
FREENULL(tmp3);
FREENULL(tmp2);
FREENULL(tmp1);
FREENULL(st);
hash = malloc(SHA256_DIGEST_LENGTH);
if (!hash)
quithere(1, "malloc OOM");
HMAC_CTX_init(&ctx);
HMAC_Init_ex(&ctx, bin, binlen, EVP_sha256(), NULL);
HMAC_Update(&ctx, (unsigned char *)&tim, sizeof(tim));
HMAC_Final(&ctx, hash, &reslen);
LOGDEBUG("%s() '%s/%s hash=%s", __func__,
st = safe_text_nonull(users->username),
USER_TOTPAUTH_NAME,
tmp1 = (char *)bin2hex(hash, SHA256_DIGEST_LENGTH));
FREENULL(tmp1);
FREENULL(st);
offset = hash[reslen-1] & 0xf;
otp = ((hash[offset] & 0x7f) << 24) | ((hash[offset+1] & 0xff) << 16) |
((hash[offset+2] & 0xff) << 8) | (hash[offset+3] & 0xff);
otp %= 1000000;
LOGDEBUG("%s() '%s/%s offset=%d otp=%"PRId32" value=%"PRId32,
__func__, st = safe_text_nonull(users->username),
USER_TOTPAUTH_NAME, offset, otp, value);
FREENULL(st);
FREENULL(hash);
if (otp == value)
return true;
else
return false;
}
bool tst_2fa(K_ITEM *old_u_item, int32_t value, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root)
{
K_ITEM *u_item;
USERS *old_users, *users;
bool ok;
DATA_USERS(old_users, old_u_item);
ok = check_2fa(old_users, value);
if (ok) {
K_WLOCK(users_free);
u_item = k_unlink_head(users_free);
K_WUNLOCK(users_free);
DATA_USERS(users, u_item);
memcpy(users, old_users, sizeof(*users));
if (users->userdata != EMPTY) {
users->userdata = strdup(users->userdata);
if (!users->userdata)
quithere(1, "strdup OOM");
}
users_userdata_del(users, USER_TEST2FA_NAME, USER_TEST2FA);
ok = users_replace(NULL, u_item, old_u_item, by, code, inet, cd,
trf_root);
// if !ok : u_item was cleaned up in user_replace()
}
return ok;
}

331
src/ckdb_data.c

@ -155,7 +155,7 @@ char *_safe_text(char *txt, bool shownull)
if (!txt) { if (!txt) {
buf = strdup("(Null)"); buf = strdup("(Null)");
if (!buf) if (!buf)
quithere(1, "malloc OOM"); quithere(1, "strdup OOM");
return buf; return buf;
} }
@ -871,6 +871,18 @@ K_ITEM *_find_create_workerstatus(int64_t userid, char *workername,
return ws_item; return ws_item;
} }
// workerstatus must be locked
static void zero_on_idle(tv_t *when, WORKERSTATUS *workerstatus)
{
copy_tv(&(workerstatus->active_start), when);
workerstatus->active_diffacc = workerstatus->active_diffinv =
workerstatus->active_diffsta = workerstatus->active_diffdup =
workerstatus->active_diffhi = workerstatus->active_diffrej =
workerstatus->active_shareacc = workerstatus->active_shareinv =
workerstatus->active_sharesta = workerstatus->active_sharedup =
workerstatus->active_sharehi = workerstatus->active_sharerej = 0.0;
}
/* All data is loaded, now update workerstatus fields /* All data is loaded, now update workerstatus fields
TODO: combine set_block_share_counters() with this? */ TODO: combine set_block_share_counters() with this? */
void workerstatus_ready() void workerstatus_ready()
@ -939,6 +951,8 @@ void _workerstatus_update(AUTHS *auths, SHARES *shares,
K_WLOCK(workerstatus_free); K_WLOCK(workerstatus_free);
if (tv_newer(&(row->last_auth), &(auths->createdate))) if (tv_newer(&(row->last_auth), &(auths->createdate)))
copy_tv(&(row->last_auth), &(auths->createdate)); copy_tv(&(row->last_auth), &(auths->createdate));
if (row->active_start.tv_sec == 0)
copy_tv(&(row->active_start), &(auths->createdate));
K_WUNLOCK(workerstatus_free); K_WUNLOCK(workerstatus_free);
} }
} }
@ -960,34 +974,54 @@ void _workerstatus_update(AUTHS *auths, SHARES *shares,
copy_tv(&(row->last_share), &(shares->createdate)); copy_tv(&(row->last_share), &(shares->createdate));
row->last_diff = shares->diff; row->last_diff = shares->diff;
} }
if (row->active_start.tv_sec == 0)
copy_tv(&(row->active_start), &(shares->createdate));
switch (shares->errn) { switch (shares->errn) {
case SE_NONE: case SE_NONE:
row->diffacc += shares->diff; row->block_diffacc += shares->diff;
row->shareacc++; row->block_shareacc++;
row->active_diffacc += shares->diff;
row->active_shareacc++;
break; break;
case SE_STALE: case SE_STALE:
row->diffinv += shares->diff; row->block_diffinv += shares->diff;
row->shareinv++; row->block_shareinv++;
row->diffsta += shares->diff; row->block_diffsta += shares->diff;
row->sharesta++; row->block_sharesta++;
row->active_diffinv += shares->diff;
row->active_shareinv++;
row->active_diffsta += shares->diff;
row->active_sharesta++;
break; break;
case SE_DUPE: case SE_DUPE:
row->diffinv += shares->diff; row->block_diffinv += shares->diff;
row->shareinv++; row->block_shareinv++;
row->diffdup += shares->diff; row->block_diffdup += shares->diff;
row->sharedup++; row->block_sharedup++;
row->active_diffinv += shares->diff;
row->active_shareinv++;
row->active_diffdup += shares->diff;
row->active_sharedup++;
break; break;
case SE_HIGH_DIFF: case SE_HIGH_DIFF:
row->diffinv += shares->diff; row->block_diffinv += shares->diff;
row->shareinv++; row->block_shareinv++;
row->diffhi += shares->diff; row->block_diffhi += shares->diff;
row->sharehi++; row->block_sharehi++;
row->active_diffinv += shares->diff;
row->active_shareinv++;
row->active_diffhi += shares->diff;
row->active_sharehi++;
break; break;
default: default:
row->diffinv += shares->diff; row->block_diffinv += shares->diff;
row->shareinv++; row->block_shareinv++;
row->diffrej += shares->diff; row->block_diffrej += shares->diff;
row->sharerej++; row->block_sharerej++;
row->active_diffinv += shares->diff;
row->active_shareinv++;
row->active_diffrej += shares->diff;
row->active_sharerej++;
break; break;
} }
K_WUNLOCK(workerstatus_free); K_WUNLOCK(workerstatus_free);
@ -1001,8 +1035,10 @@ void _workerstatus_update(AUTHS *auths, SHARES *shares,
DATA_WORKERSTATUS(row, item); DATA_WORKERSTATUS(row, item);
K_WLOCK(workerstatus_free); K_WLOCK(workerstatus_free);
if (userstats->idle) { if (userstats->idle) {
if (tv_newer(&(row->last_idle), &(userstats->statsdate))) if (tv_newer(&(row->last_idle), &(userstats->statsdate))) {
copy_tv(&(row->last_idle), &(userstats->statsdate)); copy_tv(&(row->last_idle), &(userstats->statsdate));
zero_on_idle(&(userstats->statsdate), row);
}
} else { } else {
if (tv_newer(&(row->last_stats), &(userstats->statsdate))) if (tv_newer(&(row->last_stats), &(userstats->statsdate)))
copy_tv(&(row->last_stats), &(userstats->statsdate)); copy_tv(&(row->last_stats), &(userstats->statsdate));
@ -1160,6 +1196,179 @@ bool check_hash(USERS *users, char *passwordhash)
return (strcasecmp(passwordhash, users->passwordhash) == 0); return (strcasecmp(passwordhash, users->passwordhash) == 0);
} }
static void users_checkfor(USERS *users, char *name, int64_t bits)
{
char *ptr;
ptr = strstr(users->userdata, name);
if (ptr) {
size_t len = strlen(name);
if ((ptr == users->userdata || *(ptr-1) == DATABITS_SEP) &&
*(ptr+len) == '=') {
users->databits |= bits;
}
}
}
void users_databits(USERS *users)
{
users->databits = 0;
if (users->userdata && *(users->userdata))
{
users_checkfor(users, USER_TOTPAUTH_NAME, USER_TOTPAUTH);
users_checkfor(users, USER_TEST2FA_NAME, USER_TEST2FA);
}
}
// Returns the hex text string (and length) in a malloced buffer
char *_users_userdata_get_hex(USERS *users, char *name, int64_t bit,
size_t *hexlen, WHERE_FFL_ARGS)
{
char *ptr, *tmp, *end, *st = NULL, *val = NULL;
*hexlen = 0;
if (users->userdata && (users->databits & bit)) {
ptr = strstr(users->userdata, name);
// Should always be true
if (!ptr) {
LOGEMERG("%s() users userdata/databits mismatch for "
"%s/%"PRId64 WHERE_FFL,
__func__,
st = safe_text_nonull(users->username),
users->databits, WHERE_FFL_PASS);
FREENULL(st);
} else {
tmp = ptr + strlen(name) + 1;
if ((ptr == users->userdata || *(ptr-1) == DATABITS_SEP) &&
*(tmp-1) == '=') {
end = strchr(tmp, DATABITS_SEP);
if (end)
*hexlen = end - tmp;
else
*hexlen = strlen(tmp);
val = malloc(*hexlen + 1);
if (!val)
quithere(1, "malloc OOM");
memcpy(val, tmp, *hexlen);
val[*hexlen] = '\0';
}
}
}
return val;
}
// Returns binary malloced string (and length) or NULL if not found
unsigned char *_users_userdata_get_bin(USERS *users, char *name, int64_t bit,
size_t *binlen, WHERE_FFL_ARGS)
{
unsigned char *val = NULL;
size_t hexlen;
char *hex;
*binlen = 0;
hex = _users_userdata_get_hex(users, name, bit, &hexlen,
WHERE_FFL_PASS);
if (hex) {
/* avoid calling malloc twice, hex is 2x required
* and overlap is OK with _hex2bin code */
hexlen >>= 1;
if (hex2bin(hex, hex, hexlen)) {
val = (unsigned char *)hex;
*binlen = hexlen;
} else
FREENULL(hex);
}
return val;
}
/* WARNING - users->userdata and users->databits are updated */
void _users_userdata_del(USERS *users, char *name, int64_t bit, WHERE_FFL_ARGS)
{
char *ptr, *tmp, *st = NULL, *end;
if (users->userdata && (users->databits & bit)) {
ptr = strstr(users->userdata, name);
// Should always be true
if (!ptr) {
LOGEMERG("%s() users userdata/databits mismatch for "
"%s/%"PRId64 WHERE_FFL,
__func__,
st = safe_text_nonull(users->username),
users->databits, WHERE_FFL_PASS);
FREENULL(st);
} else {
tmp = ptr + strlen(name) + 1;
if ((ptr == users->userdata || *(ptr-1) == DATABITS_SEP) &&
*(tmp-1) == '=') {
// overwrite the memory since it will be smaller
end = strchr(tmp, DATABITS_SEP);
if (!end) {
// chop off the end
if (ptr == users->userdata) {
// now empty
*ptr = '\0';
} else {
// remove from DATABITS_SEP
*(ptr-1) = '\0';
}
} else {
// overlap
memmove(ptr, end+1, strlen(end+1)+1);
}
users->databits &= ~bit;
}
}
}
}
/* hex should be null terminated hex text
* WARNING - users->userdata and users->databits are updated */
void _users_userdata_add_hex(USERS *users, char *name, int64_t bit, char *hex,
WHERE_FFL_ARGS)
{
char *ptr;
if (users->userdata && (users->databits & bit)) {
// TODO: if it's the same size or smaller, don't reallocate
_users_userdata_del(users, name, bit, WHERE_FFL_PASS);
}
if (users->userdata == EMPTY)
users->userdata = NULL;
else if (users->userdata && !(*(users->userdata)))
FREENULL(users->userdata);
if (users->userdata) {
size_t len = strlen(users->userdata) + 1 +
strlen(name) + 1 + strlen(hex) + 1;
ptr = malloc(len);
if (!(ptr))
quithere(1, "malloc OOM");
snprintf(ptr, len,
"%s%c%s=%s",
users->userdata, DATABITS_SEP, name, hex);
FREENULL(users->userdata);
users->userdata = ptr;
} else {
size_t len = strlen(name) + 1 + strlen(hex) + 1;
users->userdata = malloc(len);
if (!(users->userdata))
quithere(1, "malloc OOM");
snprintf(users->userdata, len, "%s=%s", name, hex);
}
users->databits |= bit;
}
/* value is considered binary data of length len
* WARNING - users->userdata and users->databits are updated */
void _users_userdata_add_bin(USERS *users, char *name, int64_t bit,
unsigned char *bin, size_t len, WHERE_FFL_ARGS)
{
char *hex = bin2hex((const void *)bin, len);
_users_userdata_add_hex(users, name, bit, hex, WHERE_FFL_PASS);
FREENULL(hex);
}
// default tree order by userid asc,attname asc,expirydate desc // default tree order by userid asc,attname asc,expirydate desc
cmp_t cmp_useratts(K_ITEM *a, K_ITEM *b) cmp_t cmp_useratts(K_ITEM *a, K_ITEM *b)
{ {
@ -2436,12 +2645,12 @@ void zero_on_new_block()
ws_item = first_in_ktree(workerstatus_root, ctx); ws_item = first_in_ktree(workerstatus_root, ctx);
while (ws_item) { while (ws_item) {
DATA_WORKERSTATUS(workerstatus, ws_item); DATA_WORKERSTATUS(workerstatus, ws_item);
workerstatus->diffacc = workerstatus->diffinv = workerstatus->block_diffacc = workerstatus->block_diffinv =
workerstatus->diffsta = workerstatus->diffdup = workerstatus->block_diffsta = workerstatus->block_diffdup =
workerstatus->diffhi = workerstatus->diffrej = workerstatus->block_diffhi = workerstatus->block_diffrej =
workerstatus->shareacc = workerstatus->shareinv = workerstatus->block_shareacc = workerstatus->block_shareinv =
workerstatus->sharesta = workerstatus->sharedup = workerstatus->block_sharesta = workerstatus->block_sharedup =
workerstatus->sharehi = workerstatus->sharerej = 0.0; workerstatus->block_sharehi = workerstatus->block_sharerej = 0.0;
ws_item = next_in_ktree(ctx); ws_item = next_in_ktree(ctx);
} }
K_WUNLOCK(workerstatus_free); K_WUNLOCK(workerstatus_free);
@ -2511,20 +2720,25 @@ void set_block_share_counters()
pool.diffacc += sharesummary->diffacc; pool.diffacc += sharesummary->diffacc;
pool.diffinv += sharesummary->diffsta + sharesummary->diffdup + pool.diffinv += sharesummary->diffsta + sharesummary->diffdup +
sharesummary->diffhi + sharesummary->diffrej; sharesummary->diffhi + sharesummary->diffrej;
workerstatus->diffacc += sharesummary->diffacc; // Block stats only
workerstatus->diffinv += sharesummary->diffsta + sharesummary->diffdup + workerstatus->block_diffacc += sharesummary->diffacc;
sharesummary->diffhi + sharesummary->diffrej; workerstatus->block_diffinv += sharesummary->diffsta +
workerstatus->diffsta += sharesummary->diffsta; sharesummary->diffdup +
workerstatus->diffdup += sharesummary->diffdup; sharesummary->diffhi +
workerstatus->diffhi += sharesummary->diffhi; sharesummary->diffrej;
workerstatus->diffrej += sharesummary->diffrej; workerstatus->block_diffsta += sharesummary->diffsta;
workerstatus->shareacc += sharesummary->shareacc; workerstatus->block_diffdup += sharesummary->diffdup;
workerstatus->shareinv += sharesummary->sharesta + sharesummary->sharedup + workerstatus->block_diffhi += sharesummary->diffhi;
sharesummary->sharehi + sharesummary->sharerej; workerstatus->block_diffrej += sharesummary->diffrej;
workerstatus->sharesta += sharesummary->sharesta; workerstatus->block_shareacc += sharesummary->shareacc;
workerstatus->sharedup += sharesummary->sharedup; workerstatus->block_shareinv += sharesummary->sharesta +
workerstatus->sharehi += sharesummary->sharehi; sharesummary->sharedup +
workerstatus->sharerej += sharesummary->sharerej; sharesummary->sharehi +
sharesummary->sharerej;
workerstatus->block_sharesta += sharesummary->sharesta;
workerstatus->block_sharedup += sharesummary->sharedup;
workerstatus->block_sharehi += sharesummary->sharehi;
workerstatus->block_sharerej += sharesummary->sharerej;
ss_item = prev_in_ktree(ctx); ss_item = prev_in_ktree(ctx);
} }
@ -2589,20 +2803,25 @@ void set_block_share_counters()
pool.diffacc += markersummary->diffacc; pool.diffacc += markersummary->diffacc;
pool.diffinv += markersummary->diffsta + markersummary->diffdup + pool.diffinv += markersummary->diffsta + markersummary->diffdup +
markersummary->diffhi + markersummary->diffrej; markersummary->diffhi + markersummary->diffrej;
workerstatus->diffacc += markersummary->diffacc; // Block stats only
workerstatus->diffinv += markersummary->diffsta + markersummary->diffdup + workerstatus->block_diffacc += markersummary->diffacc;
markersummary->diffhi + markersummary->diffrej; workerstatus->block_diffinv += markersummary->diffsta +
workerstatus->diffsta += markersummary->diffsta; markersummary->diffdup +
workerstatus->diffdup += markersummary->diffdup; markersummary->diffhi +
workerstatus->diffhi += markersummary->diffhi; markersummary->diffrej;
workerstatus->diffrej += markersummary->diffrej; workerstatus->block_diffsta += markersummary->diffsta;
workerstatus->shareacc += markersummary->shareacc; workerstatus->block_diffdup += markersummary->diffdup;
workerstatus->shareinv += markersummary->sharesta + markersummary->sharedup + workerstatus->block_diffhi += markersummary->diffhi;
markersummary->sharehi + markersummary->sharerej; workerstatus->block_diffrej += markersummary->diffrej;
workerstatus->sharesta += markersummary->sharesta; workerstatus->block_shareacc += markersummary->shareacc;
workerstatus->sharedup += markersummary->sharedup; workerstatus->block_shareinv += markersummary->sharesta +
workerstatus->sharehi += markersummary->sharehi; markersummary->sharedup +
workerstatus->sharerej += markersummary->sharerej; markersummary->sharehi +
markersummary->sharerej;
workerstatus->block_sharesta += markersummary->sharesta;
workerstatus->block_sharedup += markersummary->sharedup;
workerstatus->block_sharehi += markersummary->sharehi;
workerstatus->block_sharerej += markersummary->sharerej;
ms_item = prev_in_ktree(ctx_ms); ms_item = prev_in_ktree(ctx_ms);
} }
@ -2925,7 +3144,7 @@ double payout_stats(PAYOUTS *payouts, char *statname)
pos += len+1; pos += len+1;
// They should only contain +ve numbers // They should only contain +ve numbers
if (*pos && isdigit(*pos)) { if (*pos && isdigit(*pos)) {
tab = strchr(pos, '\t'); tab = strchr(pos, FLDSEP);
if (!tab) if (!tab)
numlen = strlen(pos); numlen = strlen(pos);
else else
@ -2938,7 +3157,7 @@ double payout_stats(PAYOUTS *payouts, char *statname)
} }
break; break;
} }
pos = strchr(pos, '\t'); pos = strchr(pos, FLDSEP);
if (pos) if (pos)
pos++; pos++;
} }

189
src/ckdb_dbio.c

@ -428,6 +428,11 @@ bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash,
STRNCPY(row->emailaddress, email); STRNCPY(row->emailaddress, email);
if (status) if (status)
STRNCPY(row->status, status); STRNCPY(row->status, status);
if (row->userdata != EMPTY) {
row->userdata = strdup(users->userdata);
if (!row->userdata)
quithere(1, "strdup OOM");
}
HISTORYDATEINIT(row, cd, by, code, inet); HISTORYDATEINIT(row, cd, by, code, inet);
HISTORYDATETRANSFER(trf_root, row); HISTORYDATETRANSFER(trf_root, row);
@ -478,10 +483,10 @@ bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash,
ins = "insert into users " ins = "insert into users "
"(userid,username,status,emailaddress,joineddate," "(userid,username,status,emailaddress,joineddate,"
"passwordhash,secondaryuserid,salt" "passwordhash,secondaryuserid,salt,userdata,userbits"
HISTORYDATECONTROL ") select " HISTORYDATECONTROL ") select "
"userid,username,$3,$4,joineddate," "userid,username,$3,$4,joineddate,"
"$5,secondaryuserid,$6," "$5,secondaryuserid,$6,userdata,userbits,"
"$7,$8,$9,$10,$11 from users where " "$7,$8,$9,$10,$11 from users where "
"userid=$1 and "EDDB"=$2"; "userid=$1 and "EDDB"=$2";
@ -508,9 +513,11 @@ unparam:
free(params[n]); free(params[n]);
K_WLOCK(users_free); K_WLOCK(users_free);
if (!ok) if (!ok) {
if (row->userdata != EMPTY)
FREENULL(row->userdata);
k_add_head(users_free, item); k_add_head(users_free, item);
else { } else {
users_root = remove_from_ktree(users_root, u_item, cmp_users); users_root = remove_from_ktree(users_root, u_item, cmp_users);
userid_root = remove_from_ktree(userid_root, u_item, cmp_userid); userid_root = remove_from_ktree(userid_root, u_item, cmp_userid);
copy_tv(&(users->expirydate), cd); copy_tv(&(users->expirydate), cd);
@ -527,8 +534,8 @@ unparam:
} }
K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress, K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
char *passwordhash, char *by, char *code, char *inet, char *passwordhash, int64_t userbits, char *by,
tv_t *cd, K_TREE *trf_root) char *code, char *inet, tv_t *cd, K_TREE *trf_root)
{ {
ExecStatusType rescode; ExecStatusType rescode;
bool conned = false; bool conned = false;
@ -540,7 +547,7 @@ K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
uint64_t hash; uint64_t hash;
__maybe_unused uint64_t tmp; __maybe_unused uint64_t tmp;
bool dup, ok = false; bool dup, ok = false;
char *params[8 + HISTORYDATECOUNT]; char *params[10 + HISTORYDATECOUNT];
int n, par = 0; int n, par = 0;
LOGDEBUG("%s(): add", __func__); LOGDEBUG("%s(): add", __func__);
@ -592,6 +599,9 @@ K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
row->passwordhash, sizeof(row->passwordhash)); row->passwordhash, sizeof(row->passwordhash));
} }
row->userdata = EMPTY;
row->userbits = userbits;
HISTORYDATEINIT(row, cd, by, code, inet); HISTORYDATEINIT(row, cd, by, code, inet);
HISTORYDATETRANSFER(trf_root, row); HISTORYDATETRANSFER(trf_root, row);
@ -608,13 +618,15 @@ K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
params[par++] = str_to_buf(row->passwordhash, NULL, 0); params[par++] = str_to_buf(row->passwordhash, NULL, 0);
params[par++] = str_to_buf(row->secondaryuserid, NULL, 0); params[par++] = str_to_buf(row->secondaryuserid, NULL, 0);
params[par++] = str_to_buf(row->salt, NULL, 0); params[par++] = str_to_buf(row->salt, NULL, 0);
params[par++] = str_to_buf(row->userdata, NULL, 0);
params[par++] = bigint_to_buf(row->userbits, NULL, 0);
HISTORYDATEPARAMS(params, par, row); HISTORYDATEPARAMS(params, par, row);
PARCHK(par, params); PARCHK(par, params);
ins = "insert into users " ins = "insert into users "
"(userid,username,status,emailaddress,joineddate,passwordhash," "(userid,username,status,emailaddress,joineddate,passwordhash,"
"secondaryuserid,salt" "secondaryuserid,salt,userdata,userbits"
HISTORYDATECONTROL ") values (" PQPARAM13 ")"; HISTORYDATECONTROL ") values (" PQPARAM15 ")";
if (!conn) { if (!conn) {
conn = dbconnect(); conn = dbconnect();
@ -652,6 +664,122 @@ unitem:
return NULL; return NULL;
} }
// Replace the current users record with u_item, and expire the old one
bool users_replace(PGconn *conn, K_ITEM *u_item, K_ITEM *old_u_item, char *by,
char *code, char *inet, tv_t *cd, K_TREE *trf_root)
{
ExecStatusType rescode;
bool conned = false;
PGresult *res;
bool ok = false;
USERS *users, *old_users;
char *upd, *ins;
char *params[10 + HISTORYDATECOUNT];
int n, par = 0;
LOGDEBUG("%s(): replace", __func__);
DATA_USERS(users, u_item);
DATA_USERS(old_users, old_u_item);
HISTORYDATEINIT(users, cd, by, code, inet);
HISTORYDATETRANSFER(trf_root, users);
upd = "update users set "EDDB"=$1 where userid=$2 and "EDDB"=$3";
par = 0;
params[par++] = tv_to_buf(cd, NULL, 0);
params[par++] = bigint_to_buf(old_users->userid, NULL, 0);
params[par++] = tv_to_buf((tv_t *)&default_expiry, NULL, 0);
PARCHKVAL(par, 3, params);
if (conn == NULL) {
conn = dbconnect();
conned = true;
}
// Beginning of a write txn
res = PQexec(conn, "Begin", CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Begin", rescode, conn);
goto unparam;
}
res = PQexecParams(conn, upd, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Update", rescode, conn);
goto rollback;
}
for (n = 0; n < par; n++)
free(params[n]);
par = 0;
params[par++] = bigint_to_buf(users->userid, NULL, 0);
params[par++] = str_to_buf(users->username, NULL, 0);
params[par++] = str_to_buf(users->status, NULL, 0);
params[par++] = str_to_buf(users->emailaddress, NULL, 0);
params[par++] = tv_to_buf(&(users->joineddate), NULL, 0);
params[par++] = str_to_buf(users->passwordhash, NULL, 0);
params[par++] = str_to_buf(users->secondaryuserid, NULL, 0);
params[par++] = str_to_buf(users->salt, NULL, 0);
params[par++] = str_to_buf(users->userdata, NULL, 0);
params[par++] = bigint_to_buf(users->userbits, NULL, 0);
HISTORYDATEPARAMS(params, par, users);
PARCHKVAL(par, 10 + HISTORYDATECOUNT, params);
ins = "insert into users "
"(userid,username,status,emailaddress,joineddate,"
"passwordhash,secondaryuserid,salt,userdata,userbits"
HISTORYDATECONTROL ") values (" PQPARAM15 ")";
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Insert", rescode, conn);
goto rollback;
}
ok = true;
rollback:
if (ok)
res = PQexec(conn, "Commit", CKPQ_WRITE);
else
res = PQexec(conn, "Rollback", CKPQ_WRITE);
PQclear(res);
unparam:
if (conned)
PQfinish(conn);
for (n = 0; n < par; n++)
free(params[n]);
K_WLOCK(users_free);
if (!ok) {
// cleanup done here
if (users->userdata != EMPTY)
FREENULL(users->userdata);
k_add_head(users_free, u_item);
} else {
users_root = remove_from_ktree(users_root, old_u_item, cmp_users);
userid_root = remove_from_ktree(userid_root, old_u_item, cmp_userid);
copy_tv(&(old_users->expirydate), cd);
users_root = add_to_ktree(users_root, old_u_item, cmp_users);
userid_root = add_to_ktree(userid_root, old_u_item, cmp_userid);
users_root = add_to_ktree(users_root, u_item, cmp_users);
userid_root = add_to_ktree(userid_root, u_item, cmp_userid);
k_add_head(users_store, u_item);
}
K_WUNLOCK(users_free);
return ok;
}
bool users_fill(PGconn *conn) bool users_fill(PGconn *conn)
{ {
ExecStatusType rescode; ExecStatusType rescode;
@ -661,14 +789,14 @@ bool users_fill(PGconn *conn)
USERS *row; USERS *row;
char *field; char *field;
char *sel; char *sel;
int fields = 8; int fields = 10;
bool ok; bool ok;
LOGDEBUG("%s(): select", __func__); LOGDEBUG("%s(): select", __func__);
sel = "select " sel = "select "
"userid,username,status,emailaddress,joineddate," "userid,username,status,emailaddress,joineddate,"
"passwordhash,secondaryuserid,salt" "passwordhash,secondaryuserid,salt,userdata,userbits"
HISTORYDATECONTROL HISTORYDATECONTROL
" from users"; " from users";
res = PQexec(conn, sel, CKPQ_READ); res = PQexec(conn, sel, CKPQ_READ);
@ -741,6 +869,19 @@ bool users_fill(PGconn *conn)
break; break;
TXT_TO_STR("salt", field, row->salt); TXT_TO_STR("salt", field, row->salt);
// TODO: good case for invariant
PQ_GET_FLD(res, i, "userdata", field, ok);
if (!ok)
break;
TXT_TO_PTR("userdata", field, row->userdata);
LIST_MEM_ADD(users_free, row->userdata);
users_databits(row);
PQ_GET_FLD(res, i, "userbits", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("userbits", field, row->userbits);
HISTORYDATEFLDS(res, i, row, ok); HISTORYDATEFLDS(res, i, row, ok);
if (!ok) if (!ok)
break; break;
@ -1159,7 +1300,7 @@ K_ITEM *workers_add(PGconn *conn, int64_t userid, char *workername,
K_ITEM *item, *ret = NULL; K_ITEM *item, *ret = NULL;
WORKERS *row; WORKERS *row;
char *ins; char *ins;
char *params[6 + HISTORYDATECOUNT]; char *params[7 + HISTORYDATECOUNT];
int n, par = 0; int n, par = 0;
int32_t diffdef; int32_t diffdef;
int32_t nottime; int32_t nottime;
@ -1222,6 +1363,8 @@ K_ITEM *workers_add(PGconn *conn, int64_t userid, char *workername,
if (row->idlenotificationtime == IDLENOTIFICATIONTIME_DEF) if (row->idlenotificationtime == IDLENOTIFICATIONTIME_DEF)
row->idlenotificationenabled[0] = IDLENOTIFICATIONDISABLED[0]; row->idlenotificationenabled[0] = IDLENOTIFICATIONDISABLED[0];
row->workerbits = 0;
HISTORYDATEINIT(row, cd, by, code, inet); HISTORYDATEINIT(row, cd, by, code, inet);
HISTORYDATETRANSFER(trf_root, row); HISTORYDATETRANSFER(trf_root, row);
@ -1232,13 +1375,14 @@ K_ITEM *workers_add(PGconn *conn, int64_t userid, char *workername,
params[par++] = int_to_buf(row->difficultydefault, NULL, 0); params[par++] = int_to_buf(row->difficultydefault, NULL, 0);
params[par++] = str_to_buf(row->idlenotificationenabled, NULL, 0); params[par++] = str_to_buf(row->idlenotificationenabled, NULL, 0);
params[par++] = int_to_buf(row->idlenotificationtime, NULL, 0); params[par++] = int_to_buf(row->idlenotificationtime, NULL, 0);
params[par++] = bigint_to_buf(row->workerbits, NULL, 0);
HISTORYDATEPARAMS(params, par, row); HISTORYDATEPARAMS(params, par, row);
PARCHK(par, params); PARCHK(par, params);
ins = "insert into workers " ins = "insert into workers "
"(workerid,userid,workername,difficultydefault," "(workerid,userid,workername,difficultydefault,"
"idlenotificationenabled,idlenotificationtime" "idlenotificationenabled,idlenotificationtime,workerbits"
HISTORYDATECONTROL ") values (" PQPARAM11 ")"; HISTORYDATECONTROL ") values (" PQPARAM12 ")";
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE); res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res); rescode = PQresultStatus(res);
@ -1371,8 +1515,8 @@ bool workers_update(PGconn *conn, K_ITEM *item, char *difficultydefault,
ins = "insert into workers " ins = "insert into workers "
"(workerid,userid,workername,difficultydefault," "(workerid,userid,workername,difficultydefault,"
"idlenotificationenabled,idlenotificationtime" "idlenotificationenabled,idlenotificationtime,workerbits"
HISTORYDATECONTROL ") values (" PQPARAM11 ")"; HISTORYDATECONTROL ") values (" PQPARAM12 ")";
par = 0; par = 0;
params[par++] = bigint_to_buf(row->workerid, NULL, 0); params[par++] = bigint_to_buf(row->workerid, NULL, 0);
@ -1381,6 +1525,7 @@ bool workers_update(PGconn *conn, K_ITEM *item, char *difficultydefault,
params[par++] = int_to_buf(row->difficultydefault, NULL, 0); params[par++] = int_to_buf(row->difficultydefault, NULL, 0);
params[par++] = str_to_buf(row->idlenotificationenabled, NULL, 0); params[par++] = str_to_buf(row->idlenotificationenabled, NULL, 0);
params[par++] = int_to_buf(row->idlenotificationtime, NULL, 0); params[par++] = int_to_buf(row->idlenotificationtime, NULL, 0);
params[par++] = bigint_to_buf(row->workerbits, NULL, 0);
HISTORYDATEPARAMS(params, par, row); HISTORYDATEPARAMS(params, par, row);
PARCHK(par, params); PARCHK(par, params);
@ -1418,14 +1563,14 @@ bool workers_fill(PGconn *conn)
WORKERS *row; WORKERS *row;
char *field; char *field;
char *sel; char *sel;
int fields = 6; int fields = 7;
bool ok; bool ok;
LOGDEBUG("%s(): select", __func__); LOGDEBUG("%s(): select", __func__);
sel = "select " sel = "select "
"userid,workername,difficultydefault," "userid,workername,difficultydefault,"
"idlenotificationenabled,idlenotificationtime" "idlenotificationenabled,idlenotificationtime,workerbits"
HISTORYDATECONTROL HISTORYDATECONTROL
",workerid from workers"; ",workerid from workers";
res = PQexec(conn, sel, CKPQ_READ); res = PQexec(conn, sel, CKPQ_READ);
@ -1483,6 +1628,11 @@ bool workers_fill(PGconn *conn)
break; break;
TXT_TO_INT("idlenotificationtime", field, row->idlenotificationtime); TXT_TO_INT("idlenotificationtime", field, row->idlenotificationtime);
PQ_GET_FLD(res, i, "workerbits", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("workerbits", field, row->workerbits);
HISTORYDATEFLDS(res, i, row, ok); HISTORYDATEFLDS(res, i, row, ok);
if (!ok) if (!ok)
break; break;
@ -5495,7 +5645,8 @@ bool auths_add(PGconn *conn, char *poolinstance, char *username,
if (!u_item) { if (!u_item) {
if (addressuser) { if (addressuser) {
u_item = users_add(conn, username, EMPTY, EMPTY, u_item = users_add(conn, username, EMPTY, EMPTY,
by, code, inet, cd, trf_root); USER_ADDRESS, by, code, inet, cd,
trf_root);
} else { } else {
LOGDEBUG("%s(): unknown user '%s'", LOGDEBUG("%s(): unknown user '%s'",
__func__, __func__,

55
src/ckpool.c

@ -727,37 +727,47 @@ char *_ckdb_msg_call(const ckpool_t *ckp, const char *msg, const char *file, co
return buf; return buf;
} }
static const char *rpc_method(const char *rpc_req)
{
const char *ptr = strchr(rpc_req, ':');
if (ptr)
return ptr+1;
return rpc_req;
}
json_t *json_rpc_call(connsock_t *cs, const char *rpc_req) json_t *json_rpc_call(connsock_t *cs, const char *rpc_req)
{ {
float timeout = RPC_TIMEOUT; float timeout = RPC_TIMEOUT;
char *http_req = NULL; char *http_req = NULL;
json_error_t err_val; json_error_t err_val;
json_t *val = NULL; json_t *val = NULL;
tv_t stt_tv, fin_tv;
double elapsed;
int len, ret; int len, ret;
if (unlikely(cs->fd < 0)) { if (unlikely(cs->fd < 0)) {
LOGWARNING("FD %d invalid in json_rpc_call", cs->fd); LOGWARNING("FD %d invalid in %s", cs->fd, __func__);
goto out; goto out;
} }
if (unlikely(!cs->url)) { if (unlikely(!cs->url)) {
LOGWARNING("No URL in json_rpc_call"); LOGWARNING("No URL in %s", __func__);
goto out; goto out;
} }
if (unlikely(!cs->port)) { if (unlikely(!cs->port)) {
LOGWARNING("No port in json_rpc_call"); LOGWARNING("No port in %s", __func__);
goto out; goto out;
} }
if (unlikely(!cs->auth)) { if (unlikely(!cs->auth)) {
LOGWARNING("No auth in json_rpc_call"); LOGWARNING("No auth in %s", __func__);
goto out; goto out;
} }
if (unlikely(!rpc_req)) { if (unlikely(!rpc_req)) {
LOGWARNING("Null rpc_req passed to json_rpc_call"); LOGWARNING("Null rpc_req passed to %s", __func__);
goto out; goto out;
} }
len = strlen(rpc_req); len = strlen(rpc_req);
if (unlikely(!len)) { if (unlikely(!len)) {
LOGWARNING("Zero length rpc_req passed to json_rpc_call"); LOGWARNING("Zero length rpc_req passed to %s", __func__);
goto out; goto out;
} }
http_req = ckalloc(len + 256); // Leave room for headers http_req = ckalloc(len + 256); // Leave room for headers
@ -770,31 +780,52 @@ json_t *json_rpc_call(connsock_t *cs, const char *rpc_req)
cs->auth, cs->url, cs->port, len, rpc_req); cs->auth, cs->url, cs->port, len, rpc_req);
len = strlen(http_req); len = strlen(http_req);
tv_time(&stt_tv);
ret = write_socket(cs->fd, http_req, len); ret = write_socket(cs->fd, http_req, len);
if (ret != len) { if (ret != len) {
LOGWARNING("Failed to write to socket in json_rpc_call"); tv_time(&fin_tv);
elapsed = tvdiff(&fin_tv, &stt_tv);
LOGWARNING("Failed to write to socket in %s (%.10s...) %.3fs",
__func__, rpc_method(rpc_req), elapsed);
goto out_empty; goto out_empty;
} }
ret = read_socket_line(cs, &timeout); ret = read_socket_line(cs, &timeout);
if (ret < 1) { if (ret < 1) {
LOGWARNING("Failed to read socket line in json_rpc_call"); tv_time(&fin_tv);
elapsed = tvdiff(&fin_tv, &stt_tv);
LOGWARNING("Failed to read socket line in %s (%.10s...) %.3fs",
__func__, rpc_method(rpc_req), elapsed);
goto out_empty; goto out_empty;
} }
if (strncasecmp(cs->buf, "HTTP/1.1 200 OK", 15)) { if (strncasecmp(cs->buf, "HTTP/1.1 200 OK", 15)) {
LOGWARNING("HTTP response not ok: %s", cs->buf); tv_time(&fin_tv);
elapsed = tvdiff(&fin_tv, &stt_tv);
LOGWARNING("HTTP response to (%.10s...) %.3fs not ok: %s",
rpc_method(rpc_req), elapsed, cs->buf);
goto out_empty; goto out_empty;
} }
do { do {
ret = read_socket_line(cs, &timeout); ret = read_socket_line(cs, &timeout);
if (ret < 1) { if (ret < 1) {
LOGWARNING("Failed to read http socket lines in json_rpc_call"); tv_time(&fin_tv);
elapsed = tvdiff(&fin_tv, &stt_tv);
LOGWARNING("Failed to read http socket lines in %s (%.10s...) %.3fs",
__func__, rpc_method(rpc_req), elapsed);
goto out_empty; goto out_empty;
} }
} while (strncmp(cs->buf, "{", 1)); } while (strncmp(cs->buf, "{", 1));
tv_time(&fin_tv);
elapsed = tvdiff(&fin_tv, &stt_tv);
if (elapsed > 5.0) {
LOGWARNING("HTTP socket read+write took %.3fs in %s (%.10s...)",
elapsed, __func__, rpc_method(rpc_req));
}
val = json_loads(cs->buf, 0, &err_val); val = json_loads(cs->buf, 0, &err_val);
if (!val) if (!val) {
LOGWARNING("JSON decode failed(%d): %s", err_val.line, err_val.text); LOGWARNING("JSON decode (%.10s...) failed(%d): %s",
rpc_method(rpc_req), err_val.line, err_val.text);
}
out_empty: out_empty:
empty_socket(cs->fd); empty_socket(cs->fd);
empty_buffer(cs); empty_buffer(cs);

22
src/libckpool.c

@ -948,17 +948,19 @@ char *_recv_unix_msg(int sockd, int timeout1, int timeout2, const char *file, co
{ {
char *buf = NULL; char *buf = NULL;
uint32_t msglen; uint32_t msglen;
int ret; int ret, ern;
ret = wait_read_select(sockd, timeout1); ret = wait_read_select(sockd, timeout1);
if (unlikely(ret < 1)) { if (unlikely(ret < 1)) {
LOGERR("Select1 failed in recv_unix_msg"); ern = errno;
LOGERR("Select1 failed in recv_unix_msg (%d)", ern);
goto out; goto out;
} }
/* Get message length */ /* Get message length */
ret = read_length(sockd, &msglen, 4); ret = read_length(sockd, &msglen, 4);
if (unlikely(ret < 4)) { if (unlikely(ret < 4)) {
LOGERR("Failed to read 4 byte length in recv_unix_msg"); ern = errno;
LOGERR("Failed to read 4 byte length in recv_unix_msg (%d?)", ern);
goto out; goto out;
} }
msglen = le32toh(msglen); msglen = le32toh(msglen);
@ -968,13 +970,15 @@ char *_recv_unix_msg(int sockd, int timeout1, int timeout2, const char *file, co
} }
ret = wait_read_select(sockd, timeout2); ret = wait_read_select(sockd, timeout2);
if (unlikely(ret < 1)) { if (unlikely(ret < 1)) {
LOGERR("Select2 failed in recv_unix_msg"); ern = errno;
LOGERR("Select2 failed in recv_unix_msg (%d)", ern);
goto out; goto out;
} }
buf = ckzalloc(msglen + 1); buf = ckzalloc(msglen + 1);
ret = read_length(sockd, buf, msglen); ret = read_length(sockd, buf, msglen);
if (unlikely(ret < (int)msglen)) { if (unlikely(ret < (int)msglen)) {
LOGERR("Failed to read %u bytes in recv_unix_msg", msglen); ern = errno;
LOGERR("Failed to read %u bytes in recv_unix_msg (%d?)", msglen, ern);
dealloc(buf); dealloc(buf);
} }
out: out:
@ -1005,7 +1009,7 @@ out:
int _write_length(int sockd, const void *buf, int len, const char *file, const char *func, const int line) int _write_length(int sockd, const void *buf, int len, const char *file, const char *func, const int line)
{ {
int ret, ofs = 0; int ret, ofs = 0, ern;
if (unlikely(len < 1)) { if (unlikely(len < 1)) {
LOGWARNING("Invalid write length of %d requested in write_length from %s %s:%d", LOGWARNING("Invalid write length of %d requested in write_length from %s %s:%d",
@ -1013,6 +1017,7 @@ int _write_length(int sockd, const void *buf, int len, const char *file, const c
return -1; return -1;
} }
if (unlikely(sockd < 0)) { if (unlikely(sockd < 0)) {
ern = errno;
LOGWARNING("Attempt to write to invalidated sock in write_length from %s %s:%d", LOGWARNING("Attempt to write to invalidated sock in write_length from %s %s:%d",
file, func, line); file, func, line);
return -1; return -1;
@ -1020,8 +1025,9 @@ int _write_length(int sockd, const void *buf, int len, const char *file, const c
while (len) { while (len) {
ret = write(sockd, buf + ofs, len); ret = write(sockd, buf + ofs, len);
if (unlikely(ret < 0)) { if (unlikely(ret < 0)) {
LOGERR("Failed to write %d bytes in write_length from %s %s:%d", ern = errno;
len, file, func, line); LOGERR("Failed to write %d bytes in write_length (%d) from %s %s:%d",
len, ern, file, func, line);
return -1; return -1;
} }
ofs += ret; ofs += ret;

Loading…
Cancel
Save