Merge branch 'master' of bitbucket.org:ckolivas/ckpool
This commit is contained in:
commit
d23dc06629
13
configure.ac
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(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(openssl/x509.h openssl/hmac.h)
|
||||
|
||||
PTHREAD_LIBS="-lpthread"
|
||||
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)
|
||||
AC_CHECK_LIB([gslcblas], [main],[GSLCBLAS=-lgslcblas],echo "Error: Required library gslcblas
|
||||
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])
|
||||
PQ_LIBS="-lpq -lgsl -lgslcblas"
|
||||
DB_LIBS="-lpq -lgsl -lgslcblas -lssl -lcrypto"
|
||||
else
|
||||
PQ_LIBS=""
|
||||
DB_LIBS=""
|
||||
fi
|
||||
AM_CONDITIONAL([WANT_CKDB], [test "x$ckdb" != "xno"])
|
||||
AC_SUBST(PQ_LIBS)
|
||||
AC_SUBST(DB_LIBS)
|
||||
|
||||
AC_OUTPUT([Makefile] [src/Makefile])
|
||||
|
||||
@ -80,7 +85,7 @@ echo " CPPFLAGS.............: $CPPFLAGS"
|
||||
echo " CFLAGS...............: $CFLAGS"
|
||||
echo " LDFLAGS..............: $LDFLAGS"
|
||||
echo " LDADD................: $PTHREAD_LIBS $MATH_LIBS $RT_LIBS $JANSSON_LIBS"
|
||||
echo " db LDADD.............: $PQ_LIBS"
|
||||
echo " db LDADD.............: $DB_LIBS"
|
||||
echo
|
||||
echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')"
|
||||
echo " prefix...............: $prefix"
|
||||
|
||||
@ -323,12 +323,12 @@ session_start();
|
||||
#
|
||||
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)
|
||||
$ans = repDecode($rep);
|
||||
usleep(100000); // Max 10x per second
|
||||
usleep(500000); // Max twice per second
|
||||
if ($rep != null && $ans['STATUS'] == 'ok')
|
||||
{
|
||||
$key = 'ckp'.rand(1000000,9999999);
|
||||
@ -352,7 +352,7 @@ function logout()
|
||||
}
|
||||
}
|
||||
#
|
||||
function requestRegister()
|
||||
function requestLoginRegReset()
|
||||
{
|
||||
$reg = getparam('Register', true);
|
||||
$reg2 = getparam('Reset', false);
|
||||
@ -397,7 +397,9 @@ function tryLogInOut()
|
||||
return;
|
||||
}
|
||||
|
||||
$valid = validUserPass($user, $pass);
|
||||
$twofa = getparam('2fa', false);
|
||||
|
||||
$valid = validUserPass($user, $pass, $twofa);
|
||||
if (!$valid)
|
||||
$loginfailed = true;
|
||||
}
|
||||
|
||||
51
pool/db.php
51
pool/db.php
@ -166,23 +166,18 @@ function homeInfo($user)
|
||||
if ($rep === false)
|
||||
$ans = false;
|
||||
else
|
||||
{
|
||||
$ans = repDecode($rep);
|
||||
// if ($ans['lastblock'] == '?')
|
||||
// {
|
||||
// $ans['lastblock'] = 1401237522;
|
||||
// $ans['lastblock'] = 1403819191;
|
||||
// $ans['lastblock'] = 1407113822;
|
||||
// }
|
||||
}
|
||||
|
||||
return $ans;
|
||||
}
|
||||
#
|
||||
function checkPass($user, $pass)
|
||||
function checkPass($user, $pass, $twofa)
|
||||
{
|
||||
$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);
|
||||
$rep = sendsockreply('checkPass', $msg);
|
||||
if (!$rep)
|
||||
@ -190,11 +185,14 @@ function checkPass($user, $pass)
|
||||
return $rep;
|
||||
}
|
||||
#
|
||||
function setPass($user, $oldpass, $newpass)
|
||||
function setPass($user, $oldpass, $newpass, $twofa)
|
||||
{
|
||||
$oldhash = myhash($oldpass);
|
||||
$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);
|
||||
$rep = sendsockreply('setPass', $msg);
|
||||
if (!$rep)
|
||||
@ -202,10 +200,12 @@ function setPass($user, $oldpass, $newpass)
|
||||
return repDecode($rep);
|
||||
}
|
||||
#
|
||||
function resetPass($user, $newpass)
|
||||
function resetPass($user, $newpass, $twofa)
|
||||
{
|
||||
$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);
|
||||
$rep = sendsockreply('resetPass', $msg);
|
||||
if (!$rep)
|
||||
@ -213,10 +213,24 @@ function resetPass($user, $newpass)
|
||||
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)
|
||||
{
|
||||
$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);
|
||||
$rep = sendsockreply('userReg', $msg);
|
||||
if (!$rep)
|
||||
@ -224,7 +238,7 @@ function userReg($user, $email, $pass)
|
||||
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;
|
||||
$flds = array('username' => $user);
|
||||
@ -246,7 +260,12 @@ function userSettings($user, $email = null, $addr = null, $pass = null)
|
||||
$tmo = 3; # 3x the timeout
|
||||
}
|
||||
if ($pass != null)
|
||||
{
|
||||
$flds['passwordhash'] = myhash($pass);
|
||||
if (nuem($twofa))
|
||||
$twofa = 0;
|
||||
$flds['2fa'] = $twofa;
|
||||
}
|
||||
$msg = msgEncode('usersettings', 'userset', $flds, $user);
|
||||
$rep = sendsockreply('userSettings', $msg, $tmo);
|
||||
if (!$rep)
|
||||
|
||||
@ -91,6 +91,8 @@ span.login {float:right;margin-left:8px;margin-right:24px;}
|
||||
span.hil {color:blue;}
|
||||
span.user {color:green;}
|
||||
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.urg {color:red;font-weight:bold;}
|
||||
span.err {color:red;font-weight:bold;font-size:120%;}
|
||||
|
||||
@ -165,6 +165,7 @@ function pgtop($info, $dotop, $user, $douser)
|
||||
$nlb = '?';
|
||||
$pac = '0';
|
||||
$per = '0';
|
||||
$perset = false;
|
||||
$uhr = '?GHs';
|
||||
$u1hr = '';
|
||||
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']))
|
||||
{
|
||||
$rej = $info['blockerr'];
|
||||
$per = number_format($info['blockerr'], 0);
|
||||
if (isset($info['blockacc']) && ($acc+$rej) > 0)
|
||||
$per .= ' ('.number_format(100.0*$rej/($acc+$rej), 3).'%)';
|
||||
if ($perset == false)
|
||||
$per = '';
|
||||
else
|
||||
$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']))
|
||||
@ -350,7 +385,7 @@ function pgtop($info, $dotop, $user, $douser)
|
||||
$top .= "<td class=topdat> $phr</td></tr>";
|
||||
$top .= '<tr><td class=topdes>Shares: </td>';
|
||||
$top .= "<td class=topdat> $pac</td></tr>";
|
||||
$top .= '<tr id=mini1><td class=topdes>Invalid: </td>';
|
||||
$top .= '<tr id=mini1><td class=topdes>Invalids: </td>';
|
||||
$top .= "<td class=topdat> $per</td></tr></table>";
|
||||
$top .= '</td><td>';
|
||||
$top .= '<table cellpadding=1 cellspacing=0 border=0 width=100%>';
|
||||
@ -374,17 +409,10 @@ function pgtop($info, $dotop, $user, $douser)
|
||||
list($who, $whoid) = validate();
|
||||
if ($who == false)
|
||||
{
|
||||
$top .= makeForm('')."
|
||||
<table cellpadding=0 cellspacing=0 border=0><tr><td>
|
||||
<table cellpadding=0 cellspacing=0 border=0><tr>
|
||||
<td>User:</td><td><input type=text name=User size=10 value=''></td>
|
||||
</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> <input type=submit name=Login value=Login></td></tr><tr>
|
||||
<td> <input type=submit name=Register value='Register/Reset'></td></tr></table>
|
||||
</td></tr></table></form>";
|
||||
$top .= '<table cellpadding=0 cellspacing=0 border=0><tr><td>';
|
||||
$top .= '<a href=https://' . $_SERVER['SERVER_NAME'];
|
||||
$top .= '/index.php?Register=1>Login<br>Register</a>';
|
||||
$top .= '</td></tr></table>';
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
121
pool/page_2fa.php
Normal file
121
pool/page_2fa.php
Normal file
@ -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);
|
||||
}
|
||||
#
|
||||
?>
|
||||
@ -72,13 +72,24 @@ function addrmgtuser($data, $user, $err)
|
||||
else
|
||||
$row = 'odd';
|
||||
$pg .= "<tr class=$row>";
|
||||
$pg .= '<td colspan=3 class=dc>';
|
||||
$pg .= '<td class=dr>';
|
||||
$pg .= 'Password: <input type=password name=pass size=20>';
|
||||
$pg .= ' <input type=submit name=OK value=Save></td></tr>';
|
||||
$pg .= '</td><td colspan=2> </td></tr>';
|
||||
|
||||
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 .= 'A ratio of 0, will remove the address from the payouts</font></td></tr>';
|
||||
}
|
||||
$pg .= '<tr><td colspan=3 class=dc><font size=-1><span class=st1>*</span>';
|
||||
$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 .= "</table><input type=hidden name=rows value=$count id=rows></form>\n";
|
||||
|
||||
$pg .= "<script type='text/javascript'>\n";
|
||||
@ -107,6 +118,7 @@ function doaddrmgt($data, $user)
|
||||
$OK = getparam('OK', false);
|
||||
$count = getparam('rows', false);
|
||||
$pass = getparam('pass', false);
|
||||
$twofa = getparam('2fa', false);
|
||||
if ($OK == 'Save' && !nuem($count) && !nuem($pass))
|
||||
{
|
||||
if ($count > 0 && $count < 1000)
|
||||
@ -119,7 +131,7 @@ function doaddrmgt($data, $user)
|
||||
if (!nuem($addr) && !nuem($ratio))
|
||||
$addrarr[] = array('addr' => $addr, 'ratio' => $ratio);
|
||||
}
|
||||
$ans = userSettings($user, null, $addrarr, $pass);
|
||||
$ans = userSettings($user, null, $addrarr, $pass, $twofa);
|
||||
if ($ans['STATUS'] != 'ok')
|
||||
$err = $ans['ERROR'];
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ function doallwork($data, $user)
|
||||
|
||||
$totshare = 0;
|
||||
$totdiff = 0;
|
||||
$totshrate = 0;
|
||||
$totinvalid = 0;
|
||||
$totrate = 0;
|
||||
$offset = 0;
|
||||
@ -26,13 +27,13 @@ function doallwork($data, $user)
|
||||
{
|
||||
$pg .= workuser($data, $ans['username:'.$i],
|
||||
$offset, $totshare, $totdiff,
|
||||
$totinvalid, $totrate,
|
||||
$totshrate, $totinvalid, $totrate,
|
||||
$blockacc, $blockreward, 3600);
|
||||
}
|
||||
}
|
||||
|
||||
$pg .= worktotal($offset, $totshare, $totdiff, $totinvalid, $totrate,
|
||||
$blockacc, $blockreward);
|
||||
$pg .= worktotal($offset, $totshare, $totdiff, $totshrate, $totinvalid,
|
||||
$totrate, $blockacc, $blockreward);
|
||||
|
||||
$pg .= "</table>\n";
|
||||
|
||||
|
||||
@ -204,7 +204,7 @@ function doblocks($data, $user)
|
||||
{
|
||||
$pct = 100.0 * $diffratio;
|
||||
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";
|
||||
$blktot += $diffacc;
|
||||
if ($stat != 'Orphan')
|
||||
@ -230,7 +230,7 @@ function doblocks($data, $user)
|
||||
$pg .= "<td class=dl$ex>".utcd($ans['firstcreatedate:'.$i]).'</td>';
|
||||
$pg .= "<td class=dr$ex>$stat</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 .= "</tr>\n";
|
||||
}
|
||||
|
||||
@ -2,20 +2,60 @@
|
||||
#
|
||||
function dopayout($data, $user)
|
||||
{
|
||||
$pg = '<h1>Payouts</h1>';
|
||||
$pg .= '<table width=75% cellpadding=0 cellspacing=0 border=0>';
|
||||
$pg .= '<tr><td class=dc>';
|
||||
$pg .= 'We use PPLNS (pay per last N shares)<br><br>';
|
||||
$pg .= 'The N value used for PPLNS is 5 times the network difficulty';
|
||||
$pg .= ' when a block is found,<br>';
|
||||
$pg .= 'but includes the full shift at the start and end of the range,<br>';
|
||||
$pg .= 'so it usually will be a bit more than 5N.<br><br>';
|
||||
$pg .= 'Shifts are ~50min long, however, when we find any pool blocks<br>';
|
||||
$pg .= 'the current shift ends at the point the block was found.<br>';
|
||||
$pg .= 'A ckpool restart will also start a new shift.<br><br>';
|
||||
$pg .= 'Transaction fees are included in the miner payout.<br>';
|
||||
$pg .= 'Pool fee is 0.9% of the total.<br>';
|
||||
$pg .= '</td></tr></table>';
|
||||
$t = '<span class=nn>5</span>';
|
||||
$ot = '<span class=nn>1/5</span>';
|
||||
$n = '<span class=nn>5Nd</span>';
|
||||
$n1 = '<span class=nn>N</span>';
|
||||
$n1d = '<span class=nn>Nd</span>';
|
||||
$bc = '+101 Confirms';
|
||||
$bm = 'Matured';
|
||||
$nd = 0;
|
||||
if (isset($data['info']['currndiff']))
|
||||
$nd = $data['info']['currndiff'];
|
||||
$nv = number_format($nd, 1);
|
||||
$nv5 = number_format(5.0*$nd, 1);
|
||||
|
||||
$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;
|
||||
}
|
||||
#
|
||||
|
||||
@ -15,7 +15,30 @@ function doregres($data, $u)
|
||||
else
|
||||
$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> </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>';
|
||||
if (isset($data['data']['error']))
|
||||
@ -28,14 +51,14 @@ function doregres($data, $u)
|
||||
<tr><td class=dr>Email:</td>
|
||||
<td class=dl><input name=mail value=\"$mail\"></td></tr>
|
||||
<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>
|
||||
<td class=dl><input type=password name=pass2></td></tr>
|
||||
<td class=dl><input type=password name=pass2 value=''></td></tr>
|
||||
<tr><td> </td>
|
||||
<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>
|
||||
All fields are required</font></td></tr>
|
||||
<tr><td colspan=2 class=dc><br>". passrequires() . "</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() . "</td></tr>
|
||||
</table>
|
||||
</form>";
|
||||
|
||||
@ -51,10 +74,9 @@ function doregres($data, $u)
|
||||
<td class=dl><input name=mail value=''></td></tr>
|
||||
<tr><td> </td>
|
||||
<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>
|
||||
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>
|
||||
</form>";
|
||||
|
||||
@ -103,6 +125,12 @@ function try_reg($info, $page, $menu, $name, $u)
|
||||
$ok = false;
|
||||
else
|
||||
{
|
||||
if (stripos($mail, 'hotmail') !== false)
|
||||
{
|
||||
$ok = false;
|
||||
$data['error'] = "hotmail not allowed";
|
||||
}
|
||||
|
||||
if (safepass($pass) !== true)
|
||||
{
|
||||
$ok = false;
|
||||
@ -184,9 +212,6 @@ function try_reset($info, $page, $menu, $name, $u)
|
||||
$user = getparam('user', false);
|
||||
$mail = trim(getparam('mail', false));
|
||||
|
||||
// Slow this right down
|
||||
usleep(500000);
|
||||
|
||||
$data = array();
|
||||
|
||||
if (!nuem($user))
|
||||
@ -208,6 +233,9 @@ function try_reset($info, $page, $menu, $name, $u)
|
||||
#
|
||||
function show_reg($info, $page, $menu, $name, $u)
|
||||
{
|
||||
// Slow this right down
|
||||
usleep(1000000);
|
||||
|
||||
$reg = getparam('Register', false);
|
||||
if ($reg !== NULL)
|
||||
try_reg($info, $page, $menu, $name, $u);
|
||||
|
||||
@ -20,10 +20,12 @@ function allow_reset($error)
|
||||
<td class=dl><input type=password name=pass></td></tr>
|
||||
<tr><td class=dr>Retype Password:</td>
|
||||
<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> </td>
|
||||
<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>
|
||||
</form>";
|
||||
|
||||
@ -61,6 +63,7 @@ function dbreset()
|
||||
|
||||
$pass = getparam('pass', true);
|
||||
$pass2 = getparam('pass2', true);
|
||||
$twofa = getparam('2fa', true);
|
||||
|
||||
if (nuem($pass) || nuem($pass2))
|
||||
return allow_reset('Enter both passwords');
|
||||
|
||||
@ -29,6 +29,14 @@ function settings($data, $user, $email, $addr, $err)
|
||||
$pg .= '</td><td class=dl>';
|
||||
$pg .= '<input type=password name=pass size=20>';
|
||||
$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 .= 'Change: <input type=submit name=Change value=EMail>';
|
||||
$pg .= '</td></tr>';
|
||||
@ -54,6 +62,14 @@ function settings($data, $user, $email, $addr, $err)
|
||||
$pg .= '</td><td class=dl>';
|
||||
$pg .= '<input type=password name=pass size=20>';
|
||||
$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 .= 'Change: <input type=submit name=Change value=Address>';
|
||||
$pg .= '</td></tr>';
|
||||
@ -83,6 +99,14 @@ function settings($data, $user, $email, $addr, $err)
|
||||
$pg .= '</td><td class=dl>';
|
||||
$pg .= '<input type=password name=pass2 size=20>';
|
||||
$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 .= 'Change: <input type=submit name=Change value=Password>';
|
||||
$pg .= '</td></tr>';
|
||||
@ -103,10 +127,16 @@ function dosettings($data, $user)
|
||||
{
|
||||
case 'EMail':
|
||||
$email = getparam('email', false);
|
||||
$pass = getparam('pass', false);
|
||||
$ans = userSettings($user, $email, null, $pass);
|
||||
$err = 'EMail changed';
|
||||
$check = true;
|
||||
if (stripos($email, 'hotmail') !== false)
|
||||
$err = 'hotmail not allowed';
|
||||
else
|
||||
{
|
||||
$pass = getparam('pass', false);
|
||||
$twofa = getparam('2fa', false);
|
||||
$ans = userSettings($user, $email, null, $pass, $twofa);
|
||||
$err = 'EMail changed';
|
||||
$check = true;
|
||||
}
|
||||
break;
|
||||
case 'Address':
|
||||
if (!isset($data['info']['u_multiaddr']))
|
||||
@ -114,7 +144,8 @@ function dosettings($data, $user)
|
||||
$addr = getparam('baddr', false);
|
||||
$addrarr = array(array('addr' => $addr));
|
||||
$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';
|
||||
$check = true;
|
||||
}
|
||||
@ -123,16 +154,14 @@ function dosettings($data, $user)
|
||||
$oldpass = getparam('oldpass', false);
|
||||
$pass1 = getparam('pass1', false);
|
||||
$pass2 = getparam('pass2', false);
|
||||
$twofa = getparam('2fa', false);
|
||||
if (!safepass($pass1))
|
||||
{
|
||||
$err = "Password is unsafe - requires 6 or more characters, including<br>" .
|
||||
"at least one of each uppercase, lowercase and digits, but not Tab";
|
||||
}
|
||||
$err = 'Unsafe password. ' . passrequires();
|
||||
elseif ($pass1 != $pass2)
|
||||
$err = "Passwords don't match";
|
||||
else
|
||||
{
|
||||
$ans = setPass($user, $oldpass, $pass1);
|
||||
$ans = setPass($user, $oldpass, $pass1, $twofa);
|
||||
$err = 'Password changed';
|
||||
$check = true;
|
||||
}
|
||||
@ -189,7 +218,7 @@ function dosettings($data, $user)
|
||||
$old = $_SESSION['old_set_email'];
|
||||
else
|
||||
$old = null;
|
||||
emailAddressChanged($email, zeip(), $emailinfo, $old);
|
||||
# emailAddressChanged($email, zeip(), $emailinfo, $old);
|
||||
break;
|
||||
case 'Address':
|
||||
payoutAddressChanged($email, zeip(), $emailinfo);
|
||||
|
||||
@ -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>Shares</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><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";
|
||||
return $pg;
|
||||
}
|
||||
@ -23,7 +24,7 @@ function workhashorder($a, $b)
|
||||
}
|
||||
#
|
||||
function workuser($data, $user, &$offset, &$totshare, &$totdiff,
|
||||
&$totinvalid, &$totrate, &$blockacc,
|
||||
&$totshrate, &$totinvalid, &$totrate, &$blockacc,
|
||||
&$blockreward, $old = false, $srt = false,
|
||||
$one = false, &$title)
|
||||
{
|
||||
@ -63,6 +64,8 @@ function workuser($data, $user, &$offset, &$totshare, &$totdiff,
|
||||
'w_diffacc' => $ans['w_diffacc:'.$i],
|
||||
'w_diffinv' => $ans['w_diffinv:'.$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);
|
||||
}
|
||||
|
||||
@ -92,25 +95,49 @@ function workuser($data, $user, &$offset, &$totshare, &$totdiff,
|
||||
|
||||
$shareacc = number_format($all[$i]['w_shareacc'], 0);
|
||||
$totshare += $all[$i]['w_shareacc'];
|
||||
$diffacc = number_format($all[$i]['w_diffacc'], 0);
|
||||
$ds = round($all[$i]['w_diffacc']);
|
||||
$totdiff += $all[$i]['w_diffacc'];
|
||||
$dacc = $all[$i]['w_diffacc'];
|
||||
$diffacc = number_format($dacc, 0);
|
||||
$ds = round($dacc);
|
||||
$totdiff += $dacc;
|
||||
$pg .= "<td class=dr>$shareacc</td>";
|
||||
$pg .= "<td class=dr data-srt=$ds>$diffacc</td>";
|
||||
|
||||
$dtot = $all[$i]['w_diffacc'] + $all[$i]['w_diffinv'];
|
||||
if ($dtot > 0)
|
||||
$rej = number_format(100.0 * $all[$i]['w_diffinv'] / $dtot, 3);
|
||||
else
|
||||
$rej = '0';
|
||||
$totinvalid += $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>";
|
||||
|
||||
$pg .= "<td class=dr>$rej%</td>";
|
||||
$dinv = $all[$i]['w_diffinv'];
|
||||
$dtot = $dacc + $dinv;
|
||||
if ($dtot > 0)
|
||||
{
|
||||
$rejf = $dinv / $dtot;
|
||||
$rej = number_format(100.0 * $rejf, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
$rejf = 0;
|
||||
$rej = '0';
|
||||
}
|
||||
$totinvalid += $dinv;
|
||||
|
||||
$pg .= "<td class=dr data-srt=$rejf>$rej%</td>";
|
||||
|
||||
if ($blockacc <= 0)
|
||||
$blkpct = ' ';
|
||||
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>";
|
||||
|
||||
@ -136,9 +163,11 @@ function workuser($data, $user, &$offset, &$totshare, &$totdiff,
|
||||
return $pg;
|
||||
}
|
||||
#
|
||||
function worktotal($offset, $totshare, $totdiff, $totinvalid, $totrate, $blockacc, $blockreward)
|
||||
function worktotal($offset, $totshare, $totdiff, $totshrate, $totinvalid,
|
||||
$totrate, $blockacc, $blockreward)
|
||||
{
|
||||
$pg = '';
|
||||
$totshrate = dsprate($totshrate);
|
||||
$totrate = dsprate($totrate);
|
||||
if (($offset % 2) == 0)
|
||||
$row = 'even';
|
||||
@ -149,6 +178,7 @@ function worktotal($offset, $totshare, $totdiff, $totinvalid, $totrate, $blockac
|
||||
$pg .= "<td class=dr>$shareacc</td>";
|
||||
$diffacc = number_format($totdiff, 0);
|
||||
$pg .= "<td class=dr>$diffacc</td>";
|
||||
$pg .= "<td class=dr>$totshrate</td>";
|
||||
$dtot = $totdiff + $totinvalid;
|
||||
if ($dtot > 0)
|
||||
$rej = number_format(100.0 * $totinvalid / $dtot, 3);
|
||||
@ -172,6 +202,7 @@ function doworker($data, $user)
|
||||
|
||||
$totshare = 0;
|
||||
$totdiff = 0;
|
||||
$totshrate = 0;
|
||||
$totinvalid = 0;
|
||||
$totrate = 0;
|
||||
$offset = 0;
|
||||
@ -179,11 +210,11 @@ function doworker($data, $user)
|
||||
$blockreward = 0;
|
||||
|
||||
$pg .= worktitle($data, $user);
|
||||
$pg .= workuser($data, $user, $offset, $totshare, $totdiff, $totinvalid,
|
||||
$totrate, $blockacc, $blockreward, false, true, true,
|
||||
$title);
|
||||
$pg .= worktotal($offset, $totshare, $totdiff, $totinvalid, $totrate,
|
||||
$blockacc, $blockreward);
|
||||
$pg .= workuser($data, $user, $offset, $totshare, $totdiff, $totshrate,
|
||||
$totinvalid, $totrate, $blockacc, $blockreward,
|
||||
false, true, true, $title);
|
||||
$pg .= worktotal($offset, $totshare, $totdiff, $totshrate, $totinvalid,
|
||||
$totrate, $blockacc, $blockreward);
|
||||
|
||||
if (false && $blockacc > 0 && $blockreward > 0)
|
||||
{
|
||||
|
||||
@ -73,7 +73,8 @@ function check()
|
||||
'Rewards' => 'mpayouts',
|
||||
'Payments' => 'payments',
|
||||
'Settings' => 'settings',
|
||||
'User Settings' => 'userset'
|
||||
'User Settings' => 'userset',
|
||||
'2FA Settings' => '2fa'
|
||||
),
|
||||
'Workers' => array(
|
||||
'Shifts' => 'shifts',
|
||||
@ -105,7 +106,7 @@ function check()
|
||||
showPage(NULL, 'reset', $dmenu, '', $who);
|
||||
else
|
||||
{
|
||||
if (requestRegister() == true)
|
||||
if (requestLoginRegReset() == true)
|
||||
showPage(NULL, 'reg', $dmenu, '', $who);
|
||||
else
|
||||
{
|
||||
|
||||
@ -21,6 +21,8 @@ CREATE TABLE users (
|
||||
passwordhash character varying(256) NOT NULL,
|
||||
secondaryuserid character varying(64) 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,
|
||||
createby character varying(64) 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
|
||||
idlenotificationenabled char DEFAULT 'n'::character varying NOT NULL,
|
||||
idlenotificationtime integer DEFAULT 10 NOT NULL,
|
||||
workerbits bigint NOT NULL,
|
||||
createdate timestamp with time zone NOT NULL,
|
||||
createby character varying(64) DEFAULT ''::character varying NOT NULL,
|
||||
createcode character varying(128) DEFAULT ''::character varying NOT NULL,
|
||||
@ -460,4 +463,4 @@ CREATE TABLE version (
|
||||
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
Normal file
38
sql/v1.0.0-v1.0.1.sql
Normal file
@ -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;
|
||||
@ -22,6 +22,6 @@ notifier_LDADD = libckpool.la @JANSSON_LIBS@
|
||||
if WANT_CKDB
|
||||
bin_PROGRAMS += ckdb
|
||||
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_LDADD = libckpool.la @JANSSON_LIBS@ @PQ_LIBS@ @MATH_LIBS@
|
||||
ckdb_crypt.c ckdb.h klist.c ktree.c klist.h ktree.h
|
||||
ckdb_LDADD = libckpool.la @JANSSON_LIBS@ @DB_LIBS@ @MATH_LIBS@
|
||||
endif
|
||||
|
||||
@ -3935,6 +3935,7 @@ static void *socketer(__maybe_unused void *arg)
|
||||
fflush(global_ckp->logfp);
|
||||
break;
|
||||
case CMD_CHKPASS:
|
||||
case CMD_2FA:
|
||||
case CMD_ADDUSER:
|
||||
case CMD_NEWPASS:
|
||||
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_NEWPASS:
|
||||
case CMD_CHKPASS:
|
||||
case CMD_2FA:
|
||||
case CMD_USERSET:
|
||||
case CMD_WORKERSET:
|
||||
case CMD_BLOCKLIST:
|
||||
|
||||
131
src/ckdb.h
131
src/ckdb.h
@ -54,8 +54,8 @@
|
||||
*/
|
||||
|
||||
#define DB_VLOCK "1"
|
||||
#define DB_VERSION "1.0.0"
|
||||
#define CKDB_VERSION DB_VERSION"-1.112"
|
||||
#define DB_VERSION "1.0.1"
|
||||
#define CKDB_VERSION DB_VERSION"-1.200"
|
||||
|
||||
#define WHERE_FFL " - from %s %s() line %d"
|
||||
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__
|
||||
@ -369,6 +369,7 @@ enum cmd_values {
|
||||
CMD_HEARTBEAT,
|
||||
CMD_NEWPASS,
|
||||
CMD_CHKPASS,
|
||||
CMD_2FA,
|
||||
CMD_USERSET,
|
||||
CMD_WORKERSET,
|
||||
CMD_POOLSTAT,
|
||||
@ -1017,6 +1018,9 @@ typedef struct users {
|
||||
char passwordhash[TXT_BIG+1];
|
||||
char secondaryuserid[TXT_SML+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;
|
||||
} USERS;
|
||||
|
||||
@ -1031,6 +1035,36 @@ typedef struct users {
|
||||
#define SALTSIZHEX 32
|
||||
#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 *userid_root;
|
||||
extern K_LIST *users_free;
|
||||
@ -1073,6 +1107,7 @@ typedef struct workers {
|
||||
int32_t difficultydefault;
|
||||
char idlenotificationenabled[TXT_FLAG+1];
|
||||
int32_t idlenotificationtime;
|
||||
int64_t workerbits; // Bitmask of worker attributes
|
||||
HISTORYDATECONTROLFIELDS;
|
||||
} WORKERS;
|
||||
|
||||
@ -1086,6 +1121,8 @@ extern K_TREE *workers_root;
|
||||
extern K_LIST *workers_free;
|
||||
extern K_STORE *workers_store;
|
||||
|
||||
// Currently no workerbits attributes
|
||||
|
||||
#define DIFFICULTYDEFAULT_MIN 10
|
||||
#define DIFFICULTYDEFAULT_MAX 0x7fffffff
|
||||
// 0 means it's not set
|
||||
@ -1735,18 +1772,32 @@ typedef struct workerstatus {
|
||||
tv_t last_stats;
|
||||
tv_t last_idle;
|
||||
// Below gets reset on each block
|
||||
double diffacc;
|
||||
double diffinv; // Non-acc
|
||||
double diffsta;
|
||||
double diffdup;
|
||||
double diffhi;
|
||||
double diffrej;
|
||||
double shareacc;
|
||||
double shareinv; // Non-acc
|
||||
double sharesta;
|
||||
double sharedup;
|
||||
double sharehi;
|
||||
double sharerej;
|
||||
double block_diffacc;
|
||||
double block_diffinv; // Non-acc
|
||||
double block_diffsta;
|
||||
double block_diffdup;
|
||||
double block_diffhi;
|
||||
double block_diffrej;
|
||||
double block_shareacc;
|
||||
double block_shareinv; // Non-acc
|
||||
double block_sharesta;
|
||||
double block_sharedup;
|
||||
double block_sharehi;
|
||||
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;
|
||||
|
||||
#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_userid(int64_t userid);
|
||||
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 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 K_ITEM *find_useratts(int64_t userid, char *attname);
|
||||
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 *inet, tv_t *cd, K_TREE *trf_root, char *status);
|
||||
extern K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
|
||||
char *passwordhash, char *by, char *code, char *inet,
|
||||
tv_t *cd, K_TREE *trf_root);
|
||||
char *passwordhash, int64_t userbits, char *by,
|
||||
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 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,
|
||||
@ -2455,4 +2536,20 @@ extern struct CMDS ckdb_cmds[];
|
||||
extern bool btc_valid_address(char *addr);
|
||||
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
|
||||
|
||||
342
src/ckdb_cmd.c
342
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),
|
||||
transfer_data(i_emailaddress),
|
||||
transfer_data(i_passwordhash),
|
||||
transfer_data(i_passwordhash), 0,
|
||||
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,
|
||||
__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] = "";
|
||||
size_t siz = sizeof(reply);
|
||||
bool ok = true;
|
||||
char *oldhash;
|
||||
int32_t value;
|
||||
USERS *users;
|
||||
|
||||
LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
|
||||
|
||||
@ -108,6 +110,10 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
|
||||
oldhash = EMPTY;
|
||||
}
|
||||
|
||||
i_2fa = require_name(trf_root, "2fa", 1, (char *)intpatt, reply, siz);
|
||||
if (!i_2fa)
|
||||
return strdup(reply);
|
||||
|
||||
if (ok) {
|
||||
i_newhash = require_name(trf_root, "newhash",
|
||||
64, (char *)hashpatt,
|
||||
@ -120,13 +126,21 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
|
||||
K_RUNLOCK(users_free);
|
||||
|
||||
if (u_item) {
|
||||
ok = users_update(NULL, u_item,
|
||||
oldhash,
|
||||
transfer_data(i_newhash),
|
||||
NULL,
|
||||
by, code, inet, now,
|
||||
trf_root,
|
||||
NULL);
|
||||
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,
|
||||
transfer_data(i_newhash),
|
||||
NULL,
|
||||
by, code, inet, now,
|
||||
trf_root,
|
||||
NULL);
|
||||
}
|
||||
} else
|
||||
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 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] = "";
|
||||
size_t siz = sizeof(reply);
|
||||
USERS *users;
|
||||
@ -160,6 +174,10 @@ static char *cmd_chkpass(__maybe_unused PGconn *conn, char *cmd, char *id,
|
||||
if (!i_passwordhash)
|
||||
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);
|
||||
u_item = find_users(transfer_data(i_username));
|
||||
K_RUNLOCK(users_free);
|
||||
@ -169,6 +187,10 @@ static char *cmd_chkpass(__maybe_unused PGconn *conn, char *cmd, char *id,
|
||||
else {
|
||||
DATA_USERS(users, u_item);
|
||||
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) {
|
||||
@ -179,13 +201,212 @@ static char *cmd_chkpass(__maybe_unused PGconn *conn, char *cmd, char *id,
|
||||
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,
|
||||
__maybe_unused tv_t *now, __maybe_unused char *by,
|
||||
__maybe_unused char *code, __maybe_unused char *inet,
|
||||
__maybe_unused tv_t *notcd, K_TREE *trf_root)
|
||||
{
|
||||
K_ITEM *i_username, *i_passwordhash, *i_rows, *i_address, *i_ratio;
|
||||
K_ITEM *i_email, *u_item, *pa_item, *old_pa_item;
|
||||
K_ITEM *i_username, *i_passwordhash, *i_2fa, *i_rows, *i_address;
|
||||
K_ITEM *i_ratio, *i_email, *u_item, *pa_item, *old_pa_item;
|
||||
char *email, *address;
|
||||
char reply[1024] = "";
|
||||
size_t siz = sizeof(reply);
|
||||
@ -263,10 +484,26 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
|
||||
"PaymentAddresses", FLDSEP, "");
|
||||
APPEND_REALLOC(answer, off, len, tmp);
|
||||
} 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))) {
|
||||
reason = "Incorrect password";
|
||||
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",
|
||||
1, (char *)mailpatt,
|
||||
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);
|
||||
if (ws_item) {
|
||||
DATA_WORKERSTATUS(workerstatus, ws_item);
|
||||
t_diffacc += workerstatus->diffacc;
|
||||
t_diffinv += workerstatus->diffinv;
|
||||
t_diffsta += workerstatus->diffsta;
|
||||
t_diffdup += workerstatus->diffdup;
|
||||
t_diffhi += workerstatus->diffhi;
|
||||
t_diffrej += workerstatus->diffrej;
|
||||
t_shareacc += workerstatus->shareacc;
|
||||
t_shareinv += workerstatus->shareinv;
|
||||
t_sharesta += workerstatus->sharesta;
|
||||
t_sharedup += workerstatus->sharedup;
|
||||
t_sharehi += workerstatus->sharehi;
|
||||
t_sharerej += workerstatus->sharerej;
|
||||
t_diffacc += workerstatus->block_diffacc;
|
||||
t_diffinv += workerstatus->block_diffinv;
|
||||
t_diffsta += workerstatus->block_diffsta;
|
||||
t_diffdup += workerstatus->block_diffdup;
|
||||
t_diffhi += workerstatus->block_diffhi;
|
||||
t_diffrej += workerstatus->block_diffrej;
|
||||
t_shareacc += workerstatus->block_shareacc;
|
||||
t_shareinv += workerstatus->block_shareinv;
|
||||
t_sharesta += workerstatus->block_sharesta;
|
||||
t_sharedup += workerstatus->block_sharedup;
|
||||
t_sharehi += workerstatus->block_sharehi;
|
||||
t_sharerej += workerstatus->block_sharerej;
|
||||
}
|
||||
|
||||
/* 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_sharesta, w_sharedup;
|
||||
double w_sharehi, w_sharerej;
|
||||
double w_active_diffacc;
|
||||
tv_t w_active_start;
|
||||
|
||||
w_hashrate5m = w_hashrate1hr =
|
||||
w_hashrate24hr = 0.0;
|
||||
@ -1699,30 +1938,34 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id,
|
||||
|
||||
if (!ws_item) {
|
||||
w_lastshare.tv_sec = 0;
|
||||
w_lastdiff = w_diffacc = w_diffinv =
|
||||
w_diffsta = w_diffdup =
|
||||
w_diffhi = w_diffrej =
|
||||
w_shareacc = w_shareinv =
|
||||
w_sharesta = w_sharedup =
|
||||
w_sharehi = w_sharerej = 0;
|
||||
w_lastdiff = w_diffacc =
|
||||
w_diffinv = w_diffsta =
|
||||
w_diffdup = w_diffhi =
|
||||
w_diffrej = w_shareacc =
|
||||
w_shareinv = w_sharesta =
|
||||
w_sharedup = w_sharehi =
|
||||
w_sharerej = w_active_diffacc = 0;
|
||||
w_active_start.tv_sec = 0;
|
||||
} else {
|
||||
DATA_WORKERSTATUS(workerstatus, ws_item);
|
||||
// It's bad to read possibly changing data
|
||||
K_RLOCK(workerstatus_free);
|
||||
w_lastshare.tv_sec = workerstatus->last_share.tv_sec;
|
||||
w_lastdiff = workerstatus->last_diff;
|
||||
w_diffacc = workerstatus->diffacc;
|
||||
w_diffinv = workerstatus->diffinv;
|
||||
w_diffsta = workerstatus->diffsta;
|
||||
w_diffdup = workerstatus->diffdup;
|
||||
w_diffhi = workerstatus->diffhi;
|
||||
w_diffrej = workerstatus->diffrej;
|
||||
w_shareacc = workerstatus->shareacc;
|
||||
w_shareinv = workerstatus->shareinv;
|
||||
w_sharesta = workerstatus->sharesta;
|
||||
w_sharedup = workerstatus->sharedup;
|
||||
w_sharehi = workerstatus->sharehi;
|
||||
w_sharerej = workerstatus->sharerej;
|
||||
w_diffacc = workerstatus->block_diffacc;
|
||||
w_diffinv = workerstatus->block_diffinv;
|
||||
w_diffsta = workerstatus->block_diffsta;
|
||||
w_diffdup = workerstatus->block_diffdup;
|
||||
w_diffhi = workerstatus->block_diffhi;
|
||||
w_diffrej = workerstatus->block_diffrej;
|
||||
w_shareacc = workerstatus->block_shareacc;
|
||||
w_shareinv = workerstatus->block_shareinv;
|
||||
w_sharesta = workerstatus->block_sharesta;
|
||||
w_sharedup = workerstatus->block_sharedup;
|
||||
w_sharehi = workerstatus->block_sharehi;
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1815,6 +2058,15 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id,
|
||||
double_to_buf(w_sharerej, reply, sizeof(reply));
|
||||
snprintf(tmp, sizeof(tmp), "w_sharerej:%d=%s%c", rows, reply, FLDSEP);
|
||||
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++;
|
||||
}
|
||||
@ -1832,7 +2084,8 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id,
|
||||
"w_lastdiff,w_diffacc,w_diffinv,"
|
||||
"w_diffsta,w_diffdup,w_diffhi,w_diffrej,"
|
||||
"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);
|
||||
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) {
|
||||
DATA_OPTIONCONTROL(optioncontrol, oc_item);
|
||||
u_item = users_add(conn, username, EMPTY,
|
||||
optioncontrol->optionvalue,
|
||||
optioncontrol->optionvalue, 0,
|
||||
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_NEWPASS, "newpass", false, false, cmd_newpass, 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_WORKERSET,"workerset", false, false, cmd_workerset, SEQ_NONE, ACCESS_WEB },
|
||||
{ CMD_POOLSTAT, "poolstats", false, true, cmd_poolstats, SEQ_POOLSTATS, ACCESS_POOL },
|
||||
|
||||
239
src/ckdb_crypt.c
Normal file
239
src/ckdb_crypt.c
Normal file
@ -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
331
src/ckdb_data.c
@ -155,7 +155,7 @@ char *_safe_text(char *txt, bool shownull)
|
||||
if (!txt) {
|
||||
buf = strdup("(Null)");
|
||||
if (!buf)
|
||||
quithere(1, "malloc OOM");
|
||||
quithere(1, "strdup OOM");
|
||||
return buf;
|
||||
}
|
||||
|
||||
@ -871,6 +871,18 @@ K_ITEM *_find_create_workerstatus(int64_t userid, char *workername,
|
||||
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
|
||||
TODO: combine set_block_share_counters() with this? */
|
||||
void workerstatus_ready()
|
||||
@ -939,6 +951,8 @@ void _workerstatus_update(AUTHS *auths, SHARES *shares,
|
||||
K_WLOCK(workerstatus_free);
|
||||
if (tv_newer(&(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);
|
||||
}
|
||||
}
|
||||
@ -960,34 +974,54 @@ void _workerstatus_update(AUTHS *auths, SHARES *shares,
|
||||
copy_tv(&(row->last_share), &(shares->createdate));
|
||||
row->last_diff = shares->diff;
|
||||
}
|
||||
if (row->active_start.tv_sec == 0)
|
||||
copy_tv(&(row->active_start), &(shares->createdate));
|
||||
switch (shares->errn) {
|
||||
case SE_NONE:
|
||||
row->diffacc += shares->diff;
|
||||
row->shareacc++;
|
||||
row->block_diffacc += shares->diff;
|
||||
row->block_shareacc++;
|
||||
row->active_diffacc += shares->diff;
|
||||
row->active_shareacc++;
|
||||
break;
|
||||
case SE_STALE:
|
||||
row->diffinv += shares->diff;
|
||||
row->shareinv++;
|
||||
row->diffsta += shares->diff;
|
||||
row->sharesta++;
|
||||
row->block_diffinv += shares->diff;
|
||||
row->block_shareinv++;
|
||||
row->block_diffsta += shares->diff;
|
||||
row->block_sharesta++;
|
||||
row->active_diffinv += shares->diff;
|
||||
row->active_shareinv++;
|
||||
row->active_diffsta += shares->diff;
|
||||
row->active_sharesta++;
|
||||
break;
|
||||
case SE_DUPE:
|
||||
row->diffinv += shares->diff;
|
||||
row->shareinv++;
|
||||
row->diffdup += shares->diff;
|
||||
row->sharedup++;
|
||||
row->block_diffinv += shares->diff;
|
||||
row->block_shareinv++;
|
||||
row->block_diffdup += shares->diff;
|
||||
row->block_sharedup++;
|
||||
row->active_diffinv += shares->diff;
|
||||
row->active_shareinv++;
|
||||
row->active_diffdup += shares->diff;
|
||||
row->active_sharedup++;
|
||||
break;
|
||||
case SE_HIGH_DIFF:
|
||||
row->diffinv += shares->diff;
|
||||
row->shareinv++;
|
||||
row->diffhi += shares->diff;
|
||||
row->sharehi++;
|
||||
row->block_diffinv += shares->diff;
|
||||
row->block_shareinv++;
|
||||
row->block_diffhi += shares->diff;
|
||||
row->block_sharehi++;
|
||||
row->active_diffinv += shares->diff;
|
||||
row->active_shareinv++;
|
||||
row->active_diffhi += shares->diff;
|
||||
row->active_sharehi++;
|
||||
break;
|
||||
default:
|
||||
row->diffinv += shares->diff;
|
||||
row->shareinv++;
|
||||
row->diffrej += shares->diff;
|
||||
row->sharerej++;
|
||||
row->block_diffinv += shares->diff;
|
||||
row->block_shareinv++;
|
||||
row->block_diffrej += shares->diff;
|
||||
row->block_sharerej++;
|
||||
row->active_diffinv += shares->diff;
|
||||
row->active_shareinv++;
|
||||
row->active_diffrej += shares->diff;
|
||||
row->active_sharerej++;
|
||||
break;
|
||||
}
|
||||
K_WUNLOCK(workerstatus_free);
|
||||
@ -1001,8 +1035,10 @@ void _workerstatus_update(AUTHS *auths, SHARES *shares,
|
||||
DATA_WORKERSTATUS(row, item);
|
||||
K_WLOCK(workerstatus_free);
|
||||
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));
|
||||
zero_on_idle(&(userstats->statsdate), row);
|
||||
}
|
||||
} else {
|
||||
if (tv_newer(&(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);
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
while (ws_item) {
|
||||
DATA_WORKERSTATUS(workerstatus, ws_item);
|
||||
workerstatus->diffacc = workerstatus->diffinv =
|
||||
workerstatus->diffsta = workerstatus->diffdup =
|
||||
workerstatus->diffhi = workerstatus->diffrej =
|
||||
workerstatus->shareacc = workerstatus->shareinv =
|
||||
workerstatus->sharesta = workerstatus->sharedup =
|
||||
workerstatus->sharehi = workerstatus->sharerej = 0.0;
|
||||
workerstatus->block_diffacc = workerstatus->block_diffinv =
|
||||
workerstatus->block_diffsta = workerstatus->block_diffdup =
|
||||
workerstatus->block_diffhi = workerstatus->block_diffrej =
|
||||
workerstatus->block_shareacc = workerstatus->block_shareinv =
|
||||
workerstatus->block_sharesta = workerstatus->block_sharedup =
|
||||
workerstatus->block_sharehi = workerstatus->block_sharerej = 0.0;
|
||||
ws_item = next_in_ktree(ctx);
|
||||
}
|
||||
K_WUNLOCK(workerstatus_free);
|
||||
@ -2511,20 +2720,25 @@ void set_block_share_counters()
|
||||
pool.diffacc += sharesummary->diffacc;
|
||||
pool.diffinv += sharesummary->diffsta + sharesummary->diffdup +
|
||||
sharesummary->diffhi + sharesummary->diffrej;
|
||||
workerstatus->diffacc += sharesummary->diffacc;
|
||||
workerstatus->diffinv += sharesummary->diffsta + sharesummary->diffdup +
|
||||
sharesummary->diffhi + sharesummary->diffrej;
|
||||
workerstatus->diffsta += sharesummary->diffsta;
|
||||
workerstatus->diffdup += sharesummary->diffdup;
|
||||
workerstatus->diffhi += sharesummary->diffhi;
|
||||
workerstatus->diffrej += sharesummary->diffrej;
|
||||
workerstatus->shareacc += sharesummary->shareacc;
|
||||
workerstatus->shareinv += sharesummary->sharesta + sharesummary->sharedup +
|
||||
sharesummary->sharehi + sharesummary->sharerej;
|
||||
workerstatus->sharesta += sharesummary->sharesta;
|
||||
workerstatus->sharedup += sharesummary->sharedup;
|
||||
workerstatus->sharehi += sharesummary->sharehi;
|
||||
workerstatus->sharerej += sharesummary->sharerej;
|
||||
// Block stats only
|
||||
workerstatus->block_diffacc += sharesummary->diffacc;
|
||||
workerstatus->block_diffinv += sharesummary->diffsta +
|
||||
sharesummary->diffdup +
|
||||
sharesummary->diffhi +
|
||||
sharesummary->diffrej;
|
||||
workerstatus->block_diffsta += sharesummary->diffsta;
|
||||
workerstatus->block_diffdup += sharesummary->diffdup;
|
||||
workerstatus->block_diffhi += sharesummary->diffhi;
|
||||
workerstatus->block_diffrej += sharesummary->diffrej;
|
||||
workerstatus->block_shareacc += sharesummary->shareacc;
|
||||
workerstatus->block_shareinv += sharesummary->sharesta +
|
||||
sharesummary->sharedup +
|
||||
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);
|
||||
}
|
||||
@ -2589,20 +2803,25 @@ void set_block_share_counters()
|
||||
pool.diffacc += markersummary->diffacc;
|
||||
pool.diffinv += markersummary->diffsta + markersummary->diffdup +
|
||||
markersummary->diffhi + markersummary->diffrej;
|
||||
workerstatus->diffacc += markersummary->diffacc;
|
||||
workerstatus->diffinv += markersummary->diffsta + markersummary->diffdup +
|
||||
markersummary->diffhi + markersummary->diffrej;
|
||||
workerstatus->diffsta += markersummary->diffsta;
|
||||
workerstatus->diffdup += markersummary->diffdup;
|
||||
workerstatus->diffhi += markersummary->diffhi;
|
||||
workerstatus->diffrej += markersummary->diffrej;
|
||||
workerstatus->shareacc += markersummary->shareacc;
|
||||
workerstatus->shareinv += markersummary->sharesta + markersummary->sharedup +
|
||||
markersummary->sharehi + markersummary->sharerej;
|
||||
workerstatus->sharesta += markersummary->sharesta;
|
||||
workerstatus->sharedup += markersummary->sharedup;
|
||||
workerstatus->sharehi += markersummary->sharehi;
|
||||
workerstatus->sharerej += markersummary->sharerej;
|
||||
// Block stats only
|
||||
workerstatus->block_diffacc += markersummary->diffacc;
|
||||
workerstatus->block_diffinv += markersummary->diffsta +
|
||||
markersummary->diffdup +
|
||||
markersummary->diffhi +
|
||||
markersummary->diffrej;
|
||||
workerstatus->block_diffsta += markersummary->diffsta;
|
||||
workerstatus->block_diffdup += markersummary->diffdup;
|
||||
workerstatus->block_diffhi += markersummary->diffhi;
|
||||
workerstatus->block_diffrej += markersummary->diffrej;
|
||||
workerstatus->block_shareacc += markersummary->shareacc;
|
||||
workerstatus->block_shareinv += markersummary->sharesta +
|
||||
markersummary->sharedup +
|
||||
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);
|
||||
}
|
||||
@ -2925,7 +3144,7 @@ double payout_stats(PAYOUTS *payouts, char *statname)
|
||||
pos += len+1;
|
||||
// They should only contain +ve numbers
|
||||
if (*pos && isdigit(*pos)) {
|
||||
tab = strchr(pos, '\t');
|
||||
tab = strchr(pos, FLDSEP);
|
||||
if (!tab)
|
||||
numlen = strlen(pos);
|
||||
else
|
||||
@ -2938,7 +3157,7 @@ double payout_stats(PAYOUTS *payouts, char *statname)
|
||||
}
|
||||
break;
|
||||
}
|
||||
pos = strchr(pos, '\t');
|
||||
pos = strchr(pos, FLDSEP);
|
||||
if (pos)
|
||||
pos++;
|
||||
}
|
||||
|
||||
189
src/ckdb_dbio.c
189
src/ckdb_dbio.c
@ -428,6 +428,11 @@ bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash,
|
||||
STRNCPY(row->emailaddress, email);
|
||||
if (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);
|
||||
HISTORYDATETRANSFER(trf_root, row);
|
||||
@ -478,10 +483,10 @@ bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash,
|
||||
|
||||
ins = "insert into users "
|
||||
"(userid,username,status,emailaddress,joineddate,"
|
||||
"passwordhash,secondaryuserid,salt"
|
||||
"passwordhash,secondaryuserid,salt,userdata,userbits"
|
||||
HISTORYDATECONTROL ") select "
|
||||
"userid,username,$3,$4,joineddate,"
|
||||
"$5,secondaryuserid,$6,"
|
||||
"$5,secondaryuserid,$6,userdata,userbits,"
|
||||
"$7,$8,$9,$10,$11 from users where "
|
||||
"userid=$1 and "EDDB"=$2";
|
||||
|
||||
@ -508,9 +513,11 @@ unparam:
|
||||
free(params[n]);
|
||||
|
||||
K_WLOCK(users_free);
|
||||
if (!ok)
|
||||
if (!ok) {
|
||||
if (row->userdata != EMPTY)
|
||||
FREENULL(row->userdata);
|
||||
k_add_head(users_free, item);
|
||||
else {
|
||||
} else {
|
||||
users_root = remove_from_ktree(users_root, u_item, cmp_users);
|
||||
userid_root = remove_from_ktree(userid_root, u_item, cmp_userid);
|
||||
copy_tv(&(users->expirydate), cd);
|
||||
@ -527,8 +534,8 @@ unparam:
|
||||
}
|
||||
|
||||
K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
|
||||
char *passwordhash, char *by, char *code, char *inet,
|
||||
tv_t *cd, K_TREE *trf_root)
|
||||
char *passwordhash, int64_t userbits, char *by,
|
||||
char *code, char *inet, tv_t *cd, K_TREE *trf_root)
|
||||
{
|
||||
ExecStatusType rescode;
|
||||
bool conned = false;
|
||||
@ -540,7 +547,7 @@ K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
|
||||
uint64_t hash;
|
||||
__maybe_unused uint64_t tmp;
|
||||
bool dup, ok = false;
|
||||
char *params[8 + HISTORYDATECOUNT];
|
||||
char *params[10 + HISTORYDATECOUNT];
|
||||
int n, par = 0;
|
||||
|
||||
LOGDEBUG("%s(): add", __func__);
|
||||
@ -592,6 +599,9 @@ K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
|
||||
row->passwordhash, sizeof(row->passwordhash));
|
||||
}
|
||||
|
||||
row->userdata = EMPTY;
|
||||
row->userbits = userbits;
|
||||
|
||||
HISTORYDATEINIT(row, cd, by, code, inet);
|
||||
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->secondaryuserid, 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);
|
||||
PARCHK(par, params);
|
||||
|
||||
ins = "insert into users "
|
||||
"(userid,username,status,emailaddress,joineddate,passwordhash,"
|
||||
"secondaryuserid,salt"
|
||||
HISTORYDATECONTROL ") values (" PQPARAM13 ")";
|
||||
"secondaryuserid,salt,userdata,userbits"
|
||||
HISTORYDATECONTROL ") values (" PQPARAM15 ")";
|
||||
|
||||
if (!conn) {
|
||||
conn = dbconnect();
|
||||
@ -652,6 +664,122 @@ unitem:
|
||||
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)
|
||||
{
|
||||
ExecStatusType rescode;
|
||||
@ -661,14 +789,14 @@ bool users_fill(PGconn *conn)
|
||||
USERS *row;
|
||||
char *field;
|
||||
char *sel;
|
||||
int fields = 8;
|
||||
int fields = 10;
|
||||
bool ok;
|
||||
|
||||
LOGDEBUG("%s(): select", __func__);
|
||||
|
||||
sel = "select "
|
||||
"userid,username,status,emailaddress,joineddate,"
|
||||
"passwordhash,secondaryuserid,salt"
|
||||
"passwordhash,secondaryuserid,salt,userdata,userbits"
|
||||
HISTORYDATECONTROL
|
||||
" from users";
|
||||
res = PQexec(conn, sel, CKPQ_READ);
|
||||
@ -741,6 +869,19 @@ bool users_fill(PGconn *conn)
|
||||
break;
|
||||
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);
|
||||
if (!ok)
|
||||
break;
|
||||
@ -1159,7 +1300,7 @@ K_ITEM *workers_add(PGconn *conn, int64_t userid, char *workername,
|
||||
K_ITEM *item, *ret = NULL;
|
||||
WORKERS *row;
|
||||
char *ins;
|
||||
char *params[6 + HISTORYDATECOUNT];
|
||||
char *params[7 + HISTORYDATECOUNT];
|
||||
int n, par = 0;
|
||||
int32_t diffdef;
|
||||
int32_t nottime;
|
||||
@ -1222,6 +1363,8 @@ K_ITEM *workers_add(PGconn *conn, int64_t userid, char *workername,
|
||||
if (row->idlenotificationtime == IDLENOTIFICATIONTIME_DEF)
|
||||
row->idlenotificationenabled[0] = IDLENOTIFICATIONDISABLED[0];
|
||||
|
||||
row->workerbits = 0;
|
||||
|
||||
HISTORYDATEINIT(row, cd, by, code, inet);
|
||||
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++] = str_to_buf(row->idlenotificationenabled, NULL, 0);
|
||||
params[par++] = int_to_buf(row->idlenotificationtime, NULL, 0);
|
||||
params[par++] = bigint_to_buf(row->workerbits, NULL, 0);
|
||||
HISTORYDATEPARAMS(params, par, row);
|
||||
PARCHK(par, params);
|
||||
|
||||
ins = "insert into workers "
|
||||
"(workerid,userid,workername,difficultydefault,"
|
||||
"idlenotificationenabled,idlenotificationtime"
|
||||
HISTORYDATECONTROL ") values (" PQPARAM11 ")";
|
||||
"idlenotificationenabled,idlenotificationtime,workerbits"
|
||||
HISTORYDATECONTROL ") values (" PQPARAM12 ")";
|
||||
|
||||
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
|
||||
rescode = PQresultStatus(res);
|
||||
@ -1371,8 +1515,8 @@ bool workers_update(PGconn *conn, K_ITEM *item, char *difficultydefault,
|
||||
|
||||
ins = "insert into workers "
|
||||
"(workerid,userid,workername,difficultydefault,"
|
||||
"idlenotificationenabled,idlenotificationtime"
|
||||
HISTORYDATECONTROL ") values (" PQPARAM11 ")";
|
||||
"idlenotificationenabled,idlenotificationtime,workerbits"
|
||||
HISTORYDATECONTROL ") values (" PQPARAM12 ")";
|
||||
|
||||
par = 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++] = str_to_buf(row->idlenotificationenabled, NULL, 0);
|
||||
params[par++] = int_to_buf(row->idlenotificationtime, NULL, 0);
|
||||
params[par++] = bigint_to_buf(row->workerbits, NULL, 0);
|
||||
HISTORYDATEPARAMS(params, par, row);
|
||||
PARCHK(par, params);
|
||||
|
||||
@ -1418,14 +1563,14 @@ bool workers_fill(PGconn *conn)
|
||||
WORKERS *row;
|
||||
char *field;
|
||||
char *sel;
|
||||
int fields = 6;
|
||||
int fields = 7;
|
||||
bool ok;
|
||||
|
||||
LOGDEBUG("%s(): select", __func__);
|
||||
|
||||
sel = "select "
|
||||
"userid,workername,difficultydefault,"
|
||||
"idlenotificationenabled,idlenotificationtime"
|
||||
"idlenotificationenabled,idlenotificationtime,workerbits"
|
||||
HISTORYDATECONTROL
|
||||
",workerid from workers";
|
||||
res = PQexec(conn, sel, CKPQ_READ);
|
||||
@ -1483,6 +1628,11 @@ bool workers_fill(PGconn *conn)
|
||||
break;
|
||||
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);
|
||||
if (!ok)
|
||||
break;
|
||||
@ -5495,7 +5645,8 @@ bool auths_add(PGconn *conn, char *poolinstance, char *username,
|
||||
if (!u_item) {
|
||||
if (addressuser) {
|
||||
u_item = users_add(conn, username, EMPTY, EMPTY,
|
||||
by, code, inet, cd, trf_root);
|
||||
USER_ADDRESS, by, code, inet, cd,
|
||||
trf_root);
|
||||
} else {
|
||||
LOGDEBUG("%s(): unknown user '%s'",
|
||||
__func__,
|
||||
|
||||
55
src/ckpool.c
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
float timeout = RPC_TIMEOUT;
|
||||
char *http_req = NULL;
|
||||
json_error_t err_val;
|
||||
json_t *val = NULL;
|
||||
tv_t stt_tv, fin_tv;
|
||||
double elapsed;
|
||||
int len, ret;
|
||||
|
||||
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;
|
||||
}
|
||||
if (unlikely(!cs->url)) {
|
||||
LOGWARNING("No URL in json_rpc_call");
|
||||
LOGWARNING("No URL in %s", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (unlikely(!cs->port)) {
|
||||
LOGWARNING("No port in json_rpc_call");
|
||||
LOGWARNING("No port in %s", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (unlikely(!cs->auth)) {
|
||||
LOGWARNING("No auth in json_rpc_call");
|
||||
LOGWARNING("No auth in %s", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (unlikely(!rpc_req)) {
|
||||
LOGWARNING("Null rpc_req passed to json_rpc_call");
|
||||
LOGWARNING("Null rpc_req passed to %s", __func__);
|
||||
goto out;
|
||||
}
|
||||
len = strlen(rpc_req);
|
||||
if (unlikely(!len)) {
|
||||
LOGWARNING("Zero length rpc_req passed to json_rpc_call");
|
||||
LOGWARNING("Zero length rpc_req passed to %s", __func__);
|
||||
goto out;
|
||||
}
|
||||
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);
|
||||
|
||||
len = strlen(http_req);
|
||||
tv_time(&stt_tv);
|
||||
ret = write_socket(cs->fd, http_req, 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;
|
||||
}
|
||||
ret = read_socket_line(cs, &timeout);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
do {
|
||||
ret = read_socket_line(cs, &timeout);
|
||||
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;
|
||||
}
|
||||
} 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);
|
||||
if (!val)
|
||||
LOGWARNING("JSON decode failed(%d): %s", err_val.line, err_val.text);
|
||||
if (!val) {
|
||||
LOGWARNING("JSON decode (%.10s...) failed(%d): %s",
|
||||
rpc_method(rpc_req), err_val.line, err_val.text);
|
||||
}
|
||||
out_empty:
|
||||
empty_socket(cs->fd);
|
||||
empty_buffer(cs);
|
||||
|
||||
@ -948,17 +948,19 @@ char *_recv_unix_msg(int sockd, int timeout1, int timeout2, const char *file, co
|
||||
{
|
||||
char *buf = NULL;
|
||||
uint32_t msglen;
|
||||
int ret;
|
||||
int ret, ern;
|
||||
|
||||
ret = wait_read_select(sockd, timeout1);
|
||||
if (unlikely(ret < 1)) {
|
||||
LOGERR("Select1 failed in recv_unix_msg");
|
||||
ern = errno;
|
||||
LOGERR("Select1 failed in recv_unix_msg (%d)", ern);
|
||||
goto out;
|
||||
}
|
||||
/* Get message length */
|
||||
ret = read_length(sockd, &msglen, 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;
|
||||
}
|
||||
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);
|
||||
if (unlikely(ret < 1)) {
|
||||
LOGERR("Select2 failed in recv_unix_msg");
|
||||
ern = errno;
|
||||
LOGERR("Select2 failed in recv_unix_msg (%d)", ern);
|
||||
goto out;
|
||||
}
|
||||
buf = ckzalloc(msglen + 1);
|
||||
ret = read_length(sockd, buf, 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);
|
||||
}
|
||||
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 ret, ofs = 0;
|
||||
int ret, ofs = 0, ern;
|
||||
|
||||
if (unlikely(len < 1)) {
|
||||
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;
|
||||
}
|
||||
if (unlikely(sockd < 0)) {
|
||||
ern = errno;
|
||||
LOGWARNING("Attempt to write to invalidated sock in write_length from %s %s:%d",
|
||||
file, func, line);
|
||||
return -1;
|
||||
@ -1020,8 +1025,9 @@ int _write_length(int sockd, const void *buf, int len, const char *file, const c
|
||||
while (len) {
|
||||
ret = write(sockd, buf + ofs, len);
|
||||
if (unlikely(ret < 0)) {
|
||||
LOGERR("Failed to write %d bytes in write_length from %s %s:%d",
|
||||
len, file, func, line);
|
||||
ern = errno;
|
||||
LOGERR("Failed to write %d bytes in write_length (%d) from %s %s:%d",
|
||||
len, ern, file, func, line);
|
||||
return -1;
|
||||
}
|
||||
ofs += ret;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user