mirror of
https://github.com/GOSTSec/sgminer
synced 2025-03-09 12:11:02 +00:00
Merge pull request #107 from kanoi/master
Allow API to restrict access by IP address + other commits
This commit is contained in:
commit
8c609579f4
24
README
24
README
@ -119,6 +119,8 @@ Usage instructions: Run "cgminer --help" to see options:
|
||||
|
||||
Usage: . [-atDdGCgIKklmpPQqrRsTouvwOchnV]
|
||||
Options for both config file and command line:
|
||||
--api-allow Allow API access (if enabled) only to the given list of IP[/Prefix] address[/subnets]
|
||||
This overrides --api-network and you must specify 127.0.0.1 if it is required
|
||||
--api-description Description placed in the API status header (default: cgminer version)
|
||||
--api-listen Listen for API requests (default: disabled)
|
||||
--api-network Allow API (if enabled) to listen on/for any address (default: only 127.0.0.1)
|
||||
@ -526,15 +528,24 @@ cgminer shuts down because of this.
|
||||
|
||||
---
|
||||
|
||||
API
|
||||
RPC API
|
||||
|
||||
If you start cgminer with the "--api-listen" option, it will listen on a
|
||||
simple TCP/IP socket for single string API requests from the same machine
|
||||
running cgminer and reply with a string and then close the socket each time
|
||||
Also, if you add the "--api-network" option, it will accept API requests
|
||||
from any network attached computer.
|
||||
If you add the "--api-network" option, it will accept API requests from any
|
||||
network attached computer.
|
||||
|
||||
The request can be either simple text or JSON.
|
||||
You can specify IP addresses/prefixes that are only allowed to access the API
|
||||
with the "--api-access" option e.g. --api-access 192.168.0.1,10.0.0/24
|
||||
will allow 192.168.0.1 or any address matching 10.0.0.*, but nothing else
|
||||
IP addresses are automatically padded with extra '.0's as needed
|
||||
Without a /prefix is the same as specifying /32
|
||||
Using the "--api-access" option overides the "--api-network" option if they
|
||||
are both specified
|
||||
With "--api-access", 127.0.0.1 is not by default given access unless specified
|
||||
|
||||
The RPC API request can be either simple text or JSON.
|
||||
|
||||
If the request is JSON (starts with '{'), it will reply with a JSON formatted
|
||||
response, otherwise it replies with text formatted as described further below.
|
||||
@ -544,7 +555,7 @@ The JSON request format required is '{"command":"CMD","parameter":"PARAM"}'
|
||||
where "CMD" is from the "Request" column below and "PARAM" would be e.g.
|
||||
the CPU/GPU number if required.
|
||||
|
||||
An example request in both formats:
|
||||
An example request in both formats to set GPU 0 fan to 80%:
|
||||
gpufan|0,80
|
||||
{"command":"gpufan","parameter":"0,80"}
|
||||
|
||||
@ -652,6 +663,9 @@ The list of requests and replies are:
|
||||
When you enable, disable or restart a GPU, you will also get Thread messages in
|
||||
the cgminer status window
|
||||
|
||||
When you switch to a different pool to the current one, you will get a
|
||||
'Switching to URL' message in the cgminer status windows
|
||||
|
||||
Obviously, the JSON format is simply just the names as given before the '='
|
||||
with the values after the '='
|
||||
|
||||
|
145
api.c
145
api.c
@ -126,6 +126,10 @@
|
||||
#ifndef SHUT_RDWR
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#endif
|
||||
|
||||
#ifndef in_addr_t
|
||||
#define in_addr_t uint32_t
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Big enough for largest API request
|
||||
@ -343,6 +347,14 @@ struct CODES {
|
||||
static int bye = 0;
|
||||
static bool ping = true;
|
||||
|
||||
struct IP4ACCESS {
|
||||
in_addr_t ip;
|
||||
in_addr_t mask;
|
||||
};
|
||||
|
||||
static struct IP4ACCESS *ipaccess = NULL;
|
||||
static int ips = 0;
|
||||
|
||||
// All replies (except BYE) start with a message
|
||||
// thus for JSON, message() inserts JSON_START at the front
|
||||
// and send_result() adds JSON_END at the end
|
||||
@ -1205,6 +1217,11 @@ static void tidyup()
|
||||
sock = INVSOCK;
|
||||
}
|
||||
|
||||
if (ipaccess != NULL) {
|
||||
free(ipaccess);
|
||||
ipaccess = NULL;
|
||||
}
|
||||
|
||||
if (msg_buffer != NULL) {
|
||||
free(msg_buffer);
|
||||
msg_buffer = NULL;
|
||||
@ -1216,6 +1233,89 @@ static void tidyup()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Interpret IP[/Prefix][,IP2[/Prefix2][,...]] --api-allow option
|
||||
*
|
||||
* N.B. IP4 addresses are by Definition 32bit big endian on all platforms
|
||||
*/
|
||||
static void setup_ipaccess()
|
||||
{
|
||||
char *buf, *ptr, *comma, *slash, *dot;
|
||||
int ipcount, mask, octet, i;
|
||||
|
||||
buf = malloc(strlen(opt_api_allow) + 1);
|
||||
if (unlikely(!buf))
|
||||
quit(1, "Failed to malloc ipaccess buf");
|
||||
|
||||
strcpy(buf, opt_api_allow);
|
||||
|
||||
ipcount = 1;
|
||||
ptr = buf;
|
||||
while (*ptr)
|
||||
if (*(ptr++) == ',')
|
||||
ipcount++;
|
||||
|
||||
// possibly more than needed, but never less
|
||||
ipaccess = calloc(ipcount, sizeof(struct IP4ACCESS));
|
||||
if (unlikely(!ipaccess))
|
||||
quit(1, "Failed to calloc ipaccess");
|
||||
|
||||
ips = 0;
|
||||
ptr = buf;
|
||||
while (ptr && *ptr) {
|
||||
while (*ptr == ' ' || *ptr == '\t')
|
||||
ptr++;
|
||||
|
||||
if (*ptr == ',') {
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
comma = strchr(ptr, ',');
|
||||
if (comma)
|
||||
*(comma++) = '\0';
|
||||
|
||||
slash = strchr(ptr, '/');
|
||||
if (!slash)
|
||||
ipaccess[ips].mask = 0xffffffff;
|
||||
else {
|
||||
*(slash++) = '\0';
|
||||
mask = atoi(slash);
|
||||
if (mask < 1 || mask > 32)
|
||||
goto popipo; // skip invalid/zero
|
||||
|
||||
ipaccess[ips].mask = 0;
|
||||
while (mask-- >= 0) {
|
||||
octet = 1 << (mask % 8);
|
||||
ipaccess[ips].mask |= (octet << (8 * (mask >> 3)));
|
||||
}
|
||||
}
|
||||
|
||||
ipaccess[ips].ip = 0; // missing default to '.0'
|
||||
for (i = 0; ptr && (i < 4); i++) {
|
||||
dot = strchr(ptr, '.');
|
||||
if (dot)
|
||||
*(dot++) = '\0';
|
||||
|
||||
octet = atoi(ptr);
|
||||
if (octet < 0 || octet > 0xff)
|
||||
goto popipo; // skip invalid
|
||||
|
||||
ipaccess[ips].ip |= (octet << (i * 8));
|
||||
|
||||
ptr = dot;
|
||||
}
|
||||
|
||||
ipaccess[ips].ip &= ipaccess[ips].mask;
|
||||
|
||||
ips++;
|
||||
popipo:
|
||||
ptr = comma;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void api(void)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
@ -1248,6 +1348,15 @@ void api(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt_api_allow) {
|
||||
setup_ipaccess();
|
||||
|
||||
if (ips == 0) {
|
||||
applog(LOG_WARNING, "API not running (no valid IPs specified)%s", UNAVAILABLE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == INVSOCK) {
|
||||
applog(LOG_ERR, "API1 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
|
||||
@ -1258,9 +1367,9 @@ void api(void)
|
||||
|
||||
serv.sin_family = AF_INET;
|
||||
|
||||
if (!opt_api_network) {
|
||||
if (!opt_api_allow && !opt_api_network) {
|
||||
serv.sin_addr.s_addr = inet_addr(localaddr);
|
||||
if (serv.sin_addr.s_addr == INVINETADDR) {
|
||||
if (serv.sin_addr.s_addr == (in_addr_t)INVINETADDR) {
|
||||
applog(LOG_ERR, "API2 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
|
||||
return;
|
||||
}
|
||||
@ -1296,10 +1405,14 @@ void api(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt_api_network)
|
||||
applog(LOG_WARNING, "API running in UNRESTRICTED access mode");
|
||||
else
|
||||
applog(LOG_WARNING, "API running in restricted access mode");
|
||||
if (opt_api_allow)
|
||||
applog(LOG_WARNING, "API running in IP access mode");
|
||||
else {
|
||||
if (opt_api_network)
|
||||
applog(LOG_WARNING, "API running in UNRESTRICTED access mode");
|
||||
else
|
||||
applog(LOG_WARNING, "API running in local access mode");
|
||||
}
|
||||
|
||||
io_buffer = malloc(MYBUFSIZ+1);
|
||||
msg_buffer = malloc(MYBUFSIZ+1);
|
||||
@ -1311,11 +1424,21 @@ void api(void)
|
||||
goto die;
|
||||
}
|
||||
|
||||
if (opt_api_network)
|
||||
addrok = true;
|
||||
else {
|
||||
connectaddr = inet_ntoa(cli.sin_addr);
|
||||
addrok = (strcmp(connectaddr, localaddr) == 0);
|
||||
addrok = false;
|
||||
if (opt_api_allow) {
|
||||
for (i = 0; i < ips; i++) {
|
||||
if ((cli.sin_addr.s_addr & ipaccess[i].mask) == ipaccess[i].ip) {
|
||||
addrok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (opt_api_network)
|
||||
addrok = true;
|
||||
else {
|
||||
connectaddr = inet_ntoa(cli.sin_addr);
|
||||
addrok = (strcmp(connectaddr, localaddr) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_debug) {
|
||||
|
13
cgminer.c
13
cgminer.c
@ -127,6 +127,7 @@ static bool opt_fail_only;
|
||||
bool opt_autofan;
|
||||
bool opt_autoengine;
|
||||
bool opt_noadl;
|
||||
char *opt_api_allow = NULL;
|
||||
char *opt_api_description = PACKAGE_STRING;
|
||||
int opt_api_port = 4028;
|
||||
bool opt_api_listen = false;
|
||||
@ -536,6 +537,13 @@ static char *set_schedtime(const char *arg, struct schedtime *st)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *set_api_allow(const char *arg)
|
||||
{
|
||||
opt_set_charp(arg, &opt_api_allow);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *set_api_description(const char *arg)
|
||||
{
|
||||
opt_set_charp(arg, &opt_api_description);
|
||||
@ -575,6 +583,9 @@ static struct opt_table opt_config_table[] = {
|
||||
#endif
|
||||
),
|
||||
#endif
|
||||
OPT_WITH_ARG("--api-allow",
|
||||
set_api_allow, NULL, NULL,
|
||||
"Allow API access only to the given list of IP[/Prefix] addresses[/subnets]"),
|
||||
OPT_WITH_ARG("--api-description",
|
||||
set_api_description, NULL, NULL,
|
||||
"Description placed in the API status header, default: cgminer version"),
|
||||
@ -2301,6 +2312,8 @@ void write_config(FILE *fcfg)
|
||||
for (i = 0; i < nDevs; i++)
|
||||
if (gpus[i].enabled)
|
||||
fprintf(fcfg, ",\n\"device\" : \"%d\"", i);
|
||||
if (opt_api_allow != NULL)
|
||||
fprintf(fcfg, ",\n\"api-allow\" : \"%s\"", opt_api_allow);
|
||||
if (strcmp(opt_api_description, PACKAGE_STRING) != 0)
|
||||
fprintf(fcfg, ",\n\"api-description\" : \"%s\"", opt_api_description);
|
||||
fputs("\n}", fcfg);
|
||||
|
@ -23,9 +23,9 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#include <libgen.h>
|
||||
|
1
miner.h
1
miner.h
@ -446,6 +446,7 @@ extern char *cgminer_path;
|
||||
extern bool opt_autofan;
|
||||
extern bool opt_autoengine;
|
||||
extern bool use_curses;
|
||||
extern char *opt_api_allow;
|
||||
extern char *opt_api_description;
|
||||
extern int opt_api_port;
|
||||
extern bool opt_api_listen;
|
||||
|
31
miner.php
31
miner.php
@ -17,8 +17,9 @@ td.sta { color:green; font-family:verdana,arial,sans; font-size:14pt; }
|
||||
</head><body bgcolor=#ecffff>
|
||||
<script type='text/javascript'>
|
||||
function pr(a,m){if(m!=null){if(!confirm(m+'?'))return}window.location="<? echo $here ?>"+a}
|
||||
function prs(a){var c=a.substr(3);var z=c.split('|',2);var m=z[0].substr(0,1).toUpperCase()+z[0].substr(1)+' GPU '+z[1];pr('?arg='+a,m)}
|
||||
function prs2(a,n){var v=document.getElementById('gi'+n).value;var c=a.substr(3);var z=c.split('|',2);var m='Set GPU '+z[1]+' '+z[0].substr(0,1).toUpperCase()+z[0].substr(1)+' to '+v;pr('?arg='+a+','+v,m)}
|
||||
function prc(a,m){pr('?arg='+a,m)}
|
||||
function prs(a){var c=a.substr(3);var z=c.split('|',2);var m=z[0].substr(0,1).toUpperCase()+z[0].substr(1)+' GPU '+z[1];prc(a,m)}
|
||||
function prs2(a,n){var v=document.getElementById('gi'+n).value;var c=a.substr(3);var z=c.split('|',2);var m='Set GPU '+z[1]+' '+z[0].substr(0,1).toUpperCase()+z[0].substr(1)+' to '+v;prc(a+','+v,m)}
|
||||
</script>
|
||||
<table width=100% height=100% border=0 cellpadding=0 cellspacing=0 summary='Mine'>
|
||||
<tr><td align=center valign=top>
|
||||
@ -201,7 +202,7 @@ function fmt($section, $name, $value)
|
||||
return $value;
|
||||
}
|
||||
#
|
||||
function details($list)
|
||||
function details($cmd, $list)
|
||||
{
|
||||
$stas = array('S' => 'Success', 'W' => 'Warning', 'I' => 'Informational', 'E' => 'Error', 'F' => 'Fatal');
|
||||
|
||||
@ -243,6 +244,9 @@ function details($list)
|
||||
echo "<td valign=bottom class=h>$name</td>";
|
||||
}
|
||||
|
||||
if ($cmd == 'pools')
|
||||
echo "<td valign=bottom class=h>Switch</td>";
|
||||
|
||||
echo '</tr>';
|
||||
|
||||
break;
|
||||
@ -257,6 +261,23 @@ function details($list)
|
||||
foreach ($values as $name => $value)
|
||||
echo '<td>'.fmt($section, $name, $value).'</td>';
|
||||
|
||||
if ($cmd == 'pools')
|
||||
{
|
||||
echo '<td>';
|
||||
|
||||
reset($values);
|
||||
$pool = current($values);
|
||||
if ($pool === false)
|
||||
echo ' ';
|
||||
else
|
||||
{
|
||||
echo "<input type=button value='Pool $pool'";
|
||||
echo " onclick='prc(\"switchpool|$pool\",\"Switch to Pool $pool\")'>";
|
||||
}
|
||||
|
||||
echo '</td>';
|
||||
}
|
||||
|
||||
echo '</tr>';
|
||||
}
|
||||
echo $te;
|
||||
@ -369,7 +390,7 @@ function process($cmds, $rd, $ro)
|
||||
}
|
||||
else
|
||||
{
|
||||
details($process);
|
||||
details($cmd, $process);
|
||||
echo '<tr><td><br><br></td></tr>';
|
||||
if ($cmd == 'devs')
|
||||
$devs = $process;
|
||||
@ -389,7 +410,7 @@ function display()
|
||||
echo "<tr><td><table cellpadding=0 cellspacing=0 border=0><tr><td>";
|
||||
echo "<input type=button value='Refresh' onclick='pr(\"\",null)'>";
|
||||
echo "</td><td width=100%> </td><td>";
|
||||
echo "<input type=button value='Quit' onclick='pr(\"?arg=quit\",\"Quit CGMiner\")'>";
|
||||
echo "<input type=button value='Quit' onclick='prc(\"quit\",\"Quit CGMiner\")'>";
|
||||
echo "</td></tr></table></td></tr>";
|
||||
|
||||
$arg = trim(getparam('arg', true));
|
||||
|
Loading…
x
Reference in New Issue
Block a user